<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Guillaume Laforge</title><link>https://glaforge.dev/</link><description>Recent content on Guillaume Laforge</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><copyright>This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</copyright><lastBuildDate>Thu, 02 Apr 2026 16:45:10 +0200</lastBuildDate><atom:link href="https://glaforge.dev/index.xml" rel="self" type="application/rss+xml"/><item><title>An ADK Java agent powered by Gemma 4</title><link>https://glaforge.dev/posts/2026/04/02/an-adk-java-agent-powered-by-gemma-4/</link><pubDate>Thu, 02 Apr 2026 16:45:10 +0200</pubDate><guid>https://glaforge.dev/posts/2026/04/02/an-adk-java-agent-powered-by-gemma-4/</guid><description>&lt;p>Today, DeepMind announced the
&lt;a href="https://blog.google/innovation-and-ai/technology/developers-tools/gemma-4/">release of Gemma 4&lt;/a>,
a very impressive and powerful new version of the
&lt;a href="https://deepmind.google/models/gemma/">Gemma family of models&lt;/a>.
As I&amp;rsquo;ve been contributing to &lt;a href="https://adk.dev/">ADK Java&lt;/a> a fair bit recently,
I was curious to see how I would configure ADK Java agents to work with Gemma 4.&lt;/p>
&lt;p>In this article, we&amp;rsquo;ll explore 3 paths:&lt;/p>
&lt;ul>
&lt;li>Calling the AI Studio API surface directly,&lt;/li>
&lt;li>Calling Gemma 4 hosted via a vLLM instance thanks to the
&lt;a href="https://developers.googleblog.com/en/adk-for-java-opening-up-to-third-party-language-models-via-langchain4j-integration/">LangChain4j bridge&lt;/a>.&lt;/li>
&lt;li>Calling Gemma 4 locally via Ollama&lt;/li>
&lt;/ul>
&lt;p>With the appropriate model weights format, we&amp;rsquo;ll also be able to run Gemma 4 locally via Ollama.
But that&amp;rsquo;s for another day.&lt;/p></description><content:encoded>
<![CDATA[<p>Today, DeepMind announced the
<a href="https://blog.google/innovation-and-ai/technology/developers-tools/gemma-4/">release of Gemma 4</a>,
a very impressive and powerful new version of the
<a href="https://deepmind.google/models/gemma/">Gemma family of models</a>.
As I&rsquo;ve been contributing to <a href="https://adk.dev/">ADK Java</a> a fair bit recently,
I was curious to see how I would configure ADK Java agents to work with Gemma 4.</p>
<p>In this article, we&rsquo;ll explore 3 paths:</p>
<ul>
<li>Calling the AI Studio API surface directly,</li>
<li>Calling Gemma 4 hosted via a vLLM instance thanks to the
<a href="https://developers.googleblog.com/en/adk-for-java-opening-up-to-third-party-language-models-via-langchain4j-integration/">LangChain4j bridge</a>.</li>
<li>Calling Gemma 4 locally via Ollama</li>
</ul>
<p>With the appropriate model weights format, we&rsquo;ll also be able to run Gemma 4 locally via Ollama.
But that&rsquo;s for another day.</p>
<h2 id="1--the-easy-case-gemma-4-on-ai-studio">1 — The Easy Case: Gemma 4 on AI Studio</h2>
<p>If you&rsquo;re using Gemma 4 via the Google AI Studio API surface,
you have to use the <code>Gemini</code> model builder and reference the model name:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Gemini<span style="color:#bbb"> </span>gemma4<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Gemini.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemma-4-31b-it&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>LlmAgent<span style="color:#bbb"> </span>agent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(gemma4)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ... instructions and tools</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Here, Gemma 4 is exposed the same way as the Gemini models, via the same API surface.
That&rsquo;s why the model is an instance of <code>Gemini</code>.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition tip">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M272 384c9.6-31.9 29.5-59.1 49.2-86.2c0 0 0 0 0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4c0 0 0 0 0 0c19.8 27.1 39.7 54.4 49.2 86.2l160 0zM192 512c44.2 0 80-35.8 80-80l0-16-160 0 0 16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z"/></svg>
        <span>Tip</span>
      </div>
      <div class="admonition-content">
        <p>In an upcoming release of ADK, we&rsquo;ll also be able to simplify the above by just setting the model string like we do for Gemini models:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>LlmAgent<span style="color:#bbb"> </span>agent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemma-4-31b-it&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ... instructions and tools</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div>
      </div>
    </div><h2 id="2--calling-a-vllm-hosted-gemma-4-via-langchain4j">2 — Calling a vLLM hosted Gemma 4 via LangChain4j</h2>
<p>During the beta testing period, internally at Google,
my colleague <a href="https://x.com/vladkol">Vlad</a> was exposing the Gemma 4 model weights
via <a href="https://docs.vllm.ai/">vLLM</a>, running inside a Google
<a href="https://docs.cloud.google.com/run/docs/configuring/services/gpu">Cloud Run instance with GPU</a>.
And I was using his endpoint to test Gemma 4 &#x1f609;</p>
<p>However, vLLM features an OpenAI-compatible API.
So Gemma 4 on vLLM needs to be called with that API surface, not with the Gemini one.</p>
<p>Fortunately, with the LangChain4j bridge I developed last year, you can configure OpenAI-compatible models,
thanks to the <code>OpenAiChatModel</code> (or the streaming variant) chat model
from <a href="https://docs.langchain4j.dev/">LangChain4j</a> to connect to the vLLM server.</p>
<h3 id="creating-a-simple-agent">Creating a Simple Agent</h3>
<p>First, we need to configure the <code>OpenAiChatModel</code> (or <code>OpenAiStreamingChatModel</code>):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ChatModel<span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>OpenAiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gg-hf-gg/gemma-4-31b-it&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(<span style="color:#4070a0">&#34;YOUR_API_KEY&#34;</span>)<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// A dummy key if not required by your vLLM setup</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">baseUrl</span>(<span style="color:#4070a0">&#34;https://your-vllm-instance/v1&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">timeout</span>(Duration.<span style="color:#4070a0">ofMinutes</span>(5))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">customParameters</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;chat_template_kwargs&#34;</span>,<span style="color:#bbb"> </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;enable_thinking&#34;</span>,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">true</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div>
    <div class="admonition important">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>
        <span>Important</span>
      </div>
      <div class="admonition-content">
        <p>For function calling (tool use) to work correctly with Gemma 4 on vLLM, as we shall see in further examples,
you <strong>must</strong> enable the <em>thinking capability</em> in the <em>chat template</em>.
This is done via the <code>chat_template_kwargs</code> / <code>enable_thinking</code> parameter,
which enables thinking but also function calling at the same time.</p>
      </div>
    </div>
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>I&rsquo;ve defined a long timeout, as the cold start to load the weights in memory can take up to 4 minutes!
But once the Cloud Run instance is <em>hot</em>, Gemma 4 replies instantly.</p>
      </div>
    </div><p>Let&rsquo;s have a look at a simple science teacher agent:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>LlmAgent<span style="color:#bbb"> </span>teacherAgent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;science-teacher&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(LangChain4j.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">chatModel</span>(model)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gg-hf-gg/gemma-4-31b-it&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You&#39;re a friendly science teacher
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        who explains concepts simply.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>We use the <code>LangChain4j.builder()</code> to wrap the OpenAI compatible chat model
as a Java class extending ADK&rsquo;s <code>BaseLlm</code> class, which is the parent class of all LLMs supported by ADK.</p>
<h3 id="adding-tools-local-java-functions">Adding Tools (Local Java Functions)</h3>
<p>Gemma 4&rsquo;s reasoning capabilities shine when you add tools.
You can expose any Java method as a tool using ADK&rsquo;s <code>FunctionTool</code>.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>LlmAgent<span style="color:#bbb"> </span>orderAgent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;order-agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(LangChain4j.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">chatModel</span>(model)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gg-hf-gg/gemma-4-31b-it&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Use the `lookup_order` tool to retrieve order details.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(FunctionTool.<span style="color:#4070a0">create</span>(<span style="color:#007020;font-weight:bold">this</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;retrieveOrder&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#555;font-weight:bold">@Annotations.Schema</span>(name<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;lookup_order&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Retrieve order details by ID&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>Map<span style="color:#666">&lt;</span>String,<span style="color:#bbb"> </span>Object<span style="color:#666">&gt;</span><span style="color:#bbb"> </span><span style="color:#06287e">retrieveOrder</span>(String<span style="color:#bbb"> </span>orderId)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// Your database logic here...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;status&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;out_for_delivery&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>In this example, we reference a local Java function to lookup order details,
so Gemma 4 can call it should the user ask for the status of their order.</p>
<h2 id="3--calling-gemma-4-locally-via-ollama">3 — Calling Gemma 4 locally via Ollama</h2>
<p>It&rsquo;s also possible to take on a third path, with <a href="https://ollama.com/library/gemma4">Ollama&rsquo;s Gemma 4 support</a>.
Thanks to the LangChain4j bridge again, you can configure Gemma 4 with the following LangChain4j chat model definition:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>OllamaChatModel<span style="color:#bbb"> </span>ollamaChatModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>OllamaChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">   </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemma4:e4b&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">   </span>.<span style="color:#4070a0">baseUrl</span>(<span style="color:#4070a0">&#34;http://127.0.0.1:11434&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">   </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="wrapping-up">Wrapping up</h2>
<p>That&rsquo;s about it for today!
With ADK Java and Gemma 4, you have a powerful, flexible, and open-weight foundation for your next AI agent project! &#x1f916;
Thanks to the LangChain4j / ADK bridge, it&rsquo;s even possible to invoke Gemma via different API surfaces than Gemini&rsquo;s.</p>

    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>As a reminder, we&rsquo;ve just
<a href="https://developers.googleblog.com/announcing-adk-for-java-100-building-the-future-of-ai-agents-in-java/">announced ADK Java 1.0</a>,
if you want to have a refresher about the latest features and enhancements to the project.</p>
<p>And you can watch this YouTube video I recorded that goes through the new features,
as well as a concrete ADK agent called &ldquo;Comic Trip&rdquo; that transforms travel photography into vintage pop-art comic illustrations.
Go check out the
<a href="content/posts/2026/03/30/building-my-comic-trip-agent-with-adk-java-1-0.md">behind-the-scene article</a> on how I built it.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/YqABMjSho_M?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

      </div>
    </div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Creating a Wikipedia MCP Server in Java in a Few Prompts with Skills</title><link>https://glaforge.dev/posts/2026/04/02/creating-a-wikipedia-mcp-server-in-java-in-a-few-prompts/</link><pubDate>Thu, 02 Apr 2026 10:17:27 +0200</pubDate><guid>https://glaforge.dev/posts/2026/04/02/creating-a-wikipedia-mcp-server-in-java-in-a-few-prompts/</guid><description>&lt;p>Since I started using &lt;a href="https://modelcontextprotocol.io/">Model Context Protocol (MCP)&lt;/a> to equip my AI agents with useful tools,
I&amp;rsquo;ve been looking for ways to quickly build and iterate on local servers.
A few weeks ago, I shared
&lt;a href="https://glaforge.dev/posts/2026/02/21/easily-build-a-local-mcp-server-in-java-with-a-skill-in-gemini-cli/">how to easily build a local MCP server in Java with a custom skill in Gemini CLI&lt;/a>.
Today, I wanted to put that skill to the test by creating a &lt;strong>Wikipedia MCP server&lt;/strong>.&lt;/p>
&lt;p>What&amp;rsquo;s impressive is that I didn&amp;rsquo;t even have to leave my terminal or read documentation.
The entire process was a conversation with &lt;a href="https://geminicli.com">Gemini CLI&lt;/a>, leveraging its ability to search the web, find libraries, and even check migration guides!&lt;/p></description><content:encoded>
<![CDATA[<p>Since I started using <a href="https://modelcontextprotocol.io/">Model Context Protocol (MCP)</a> to equip my AI agents with useful tools,
I&rsquo;ve been looking for ways to quickly build and iterate on local servers.
A few weeks ago, I shared
<a href="https://glaforge.dev/posts/2026/02/21/easily-build-a-local-mcp-server-in-java-with-a-skill-in-gemini-cli/">how to easily build a local MCP server in Java with a custom skill in Gemini CLI</a>.
Today, I wanted to put that skill to the test by creating a <strong>Wikipedia MCP server</strong>.</p>
<p>What&rsquo;s impressive is that I didn&rsquo;t even have to leave my terminal or read documentation.
The entire process was a conversation with <a href="https://geminicli.com">Gemini CLI</a>, leveraging its ability to search the web, find libraries, and even check migration guides!</p>
<h2 id="the-interactive-process">The Interactive Process</h2>
<p>I started by asking Gemini CLI about the Wikipedia API.
Instead of guessing, I used the <code>@search</code> command to find the exact <em>&ldquo;contracts&rdquo;</em> for searching and retrieving pages.</p>
<p>The conversation went something like this:</p>
<ol>
<li>
<p><strong>Exploring the API</strong>: I asked <code>@search what is the contract for the Wikipedia API to search for Wikipedia pages?</code>. Gemini found the modern <strong>Wikimedia REST API</strong> (<code>/search/page</code>) and the older Action API.
<figure>
  <a href="#img-64a81fd5cd10fbc36493595ae8d2ce83">
    <img src="/img/gemini-cli/wikipedia-mcp/gcli-mcp-wikipedia-1.jpg"
      alt="Using @search in Gemini CLI to find information about the Wikipedia API"
       />
  </a>
  <figcaption>Using @search in Gemini CLI to find information about the Wikipedia API</figcaption>
</figure>
<div class="lightbox" id="img-64a81fd5cd10fbc36493595ae8d2ce83">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/wikipedia-mcp/gcli-mcp-wikipedia-1.jpg"
    alt="Using @search in Gemini CLI to find information about the Wikipedia API"
     />
  <div class="lightbox-caption">Using @search in Gemini CLI to find information about the Wikipedia API</div>
</div>
</p>
</li>
<li>
<p><strong>Retrieving Content</strong>: I then asked how to get the actual page content. It identified the <code>/page/html/{title}</code> endpoint as the best way to get clean HTML.
<figure>
  <a href="#img-99cfb0fd769ba27247e6a98996bb39b5">
    <img src="/img/gemini-cli/wikipedia-mcp/gcli-mcp-wikipedia-2.jpg"
      alt="Gemini CLI screenshot showing the search about the full Wikipedia page retrieval"
       />
  </a>
  <figcaption>Gemini CLI screenshot showing the search about the full Wikipedia page retrieval</figcaption>
</figure>
<div class="lightbox" id="img-99cfb0fd769ba27247e6a98996bb39b5">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/wikipedia-mcp/gcli-mcp-wikipedia-2.jpg"
    alt="Gemini CLI screenshot showing the search about the full Wikipedia page retrieval"
     />
  <div class="lightbox-caption">Gemini CLI screenshot showing the search about the full Wikipedia page retrieval</div>
</div>
</p>
</li>
<li>
<p><strong>Finding a Converter</strong>: Since LLMs prefer Markdown over raw HTML (returned by the Wikipedia API), I searched for a Java library: <code>@search how to render HTML to Markdown in Java?</code>. It suggested <strong>CopyDown</strong> (a Java port of Turndown) as the simplest option.
<figure>
  <a href="#img-ebb2484935ce04d010f2dc903cb597bc">
    <img src="/img/gemini-cli/wikipedia-mcp/gcli-mcp-wikipedia-3.jpg"
      alt="Gemini CLI interactive session on how to find an HTML to Markdown converter"
       />
  </a>
  <figcaption>Gemini CLI interactive session on how to find an HTML to Markdown converter</figcaption>
</figure>
<div class="lightbox" id="img-ebb2484935ce04d010f2dc903cb597bc">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/wikipedia-mcp/gcli-mcp-wikipedia-3.jpg"
    alt="Gemini CLI interactive session on how to find an HTML to Markdown converter"
     />
  <div class="lightbox-caption">Gemini CLI interactive session on how to find an HTML to Markdown converter</div>
</div>
</p>
</li>
</ol>
<p>One particularly &ldquo;pro&rdquo; move from Gemini: it noticed I wanted to use <strong>Jackson</strong> for JSON parsing.
It proactively searched for the latest version, found that <strong>Jackson 3.0.0-rc4</strong> was just released,
and even checked the <a href="https://github.com/FasterXML/jackson/blob/main/jackson3/MIGRATING_TO_JACKSON_3.md">migration guide</a>
to ensure the new <code>tools.jackson</code> package names were used correctly!</p>
<p>Then, I asked Gemini CLI to use my JBang / LangChain4j MCP server creation skill to generate the code of my Wikipedia STDIO MCP server:
<figure>
  <a href="#img-3127413bc24c5d5f1d03688a87a3b9f8">
    <img src="/img/gemini-cli/wikipedia-mcp/gcli-mcp-wikipedia-4.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-3127413bc24c5d5f1d03688a87a3b9f8">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/wikipedia-mcp/gcli-mcp-wikipedia-4.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h2 id="the-resulting-java-code">The Resulting Java Code</h2>
<p>Once we had the blueprint, it triggered my <code>jbang-mcp-server</code> skill.
It scaffolded the following <a href="https://jbang.dev/">JBang</a> script,
combining <a href="https://github.com/langchain4j/langchain4j">LangChain4j</a>&rsquo;s MCP support with our chosen libraries.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">///usr/bin/env jbang &#34;$0&#34; &#34;$@&#34; ; exit $?</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//DEPS dev.langchain4j:langchain4j-core:1.11.0</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//DEPS dev.langchain4j:langchain4j-community-mcp-server:1.11.0-beta19</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//DEPS org.slf4j:slf4j-simple:2.0.17</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//DEPS io.github.furstenheim:copy_down:1.1</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//DEPS tools.jackson.core:jackson-databind:3.0.0-rc4</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//JAVA 21</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.agent.tool.Tool</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.community.mcp.server.McpServer</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.community.mcp.server.transport.StdioMcpServerTransport</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">org.slf4j.Logger</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">org.slf4j.LoggerFactory</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.github.furstenheim.CopyDown</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">tools.jackson.databind.ObjectMapper</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">tools.jackson.databind.JsonNode</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">tools.jackson.databind.json.JsonMapper</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.net.URI</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.net.http.HttpClient</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.net.http.HttpRequest</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.net.http.HttpResponse</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.List</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.concurrent.CountDownLatch</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.net.URLEncoder</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.nio.charset.StandardCharsets</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">WikipediaMcpServer</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// Configure SLF4J Simple Logger to write to System.err</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// This is crucial for MCP servers over STDIO to avoid polluting stdout</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>System.<span style="color:#4070a0">setProperty</span>(<span style="color:#4070a0">&#34;org.slf4j.simpleLogger.logFile&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;System.err&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>Logger<span style="color:#bbb"> </span>log<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LoggerFactory.<span style="color:#4070a0">getLogger</span>(WikipediaMcpServer.<span style="color:#4070a0">class</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">main</span>(String<span style="color:#666">[]</span><span style="color:#bbb"> </span>args)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">throws</span><span style="color:#bbb"> </span>Exception<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Starting Wikipedia MCP Server...&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>WikipediaTools<span style="color:#bbb"> </span>tools<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>WikipediaTools();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>McpServer<span style="color:#bbb"> </span>server<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>McpServer(List.<span style="color:#4070a0">of</span>(tools));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>StdioMcpServerTransport<span style="color:#bbb"> </span>transport<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>StdioMcpServerTransport(server);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;MCP Server started successfully on STDIO.&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>CountDownLatch(1).<span style="color:#4070a0">await</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">WikipediaTools</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>HttpClient<span style="color:#bbb"> </span>httpClient<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>HttpClient.<span style="color:#4070a0">newBuilder</span>().<span style="color:#4070a0">followRedirects</span>(HttpClient.<span style="color:#4070a0">Redirect</span>.<span style="color:#4070a0">NORMAL</span>).<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>ObjectMapper<span style="color:#bbb"> </span>objectMapper<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>JsonMapper.<span style="color:#4070a0">builder</span>().<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>CopyDown<span style="color:#bbb"> </span>copyDown<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>CopyDown();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>USER_AGENT<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;WikipediaMcpServer/1.0 (contact@example.com)&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@Tool</span>(<span style="color:#4070a0">&#34;Search Wikipedia for a given query and return a list of matching page titles and brief descriptions. Use this to find the exact page title before retrieving content.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">searchWikipedia</span>(String<span style="color:#bbb"> </span>query)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">throws</span><span style="color:#bbb"> </span>Exception<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Searching Wikipedia for: {}&#34;</span>,<span style="color:#bbb"> </span>query);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>String<span style="color:#bbb"> </span>encodedQuery<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>URLEncoder.<span style="color:#4070a0">encode</span>(query,<span style="color:#bbb"> </span>StandardCharsets.<span style="color:#4070a0">UTF_8</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>String<span style="color:#bbb"> </span>url<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;https://en.wikipedia.org/w/rest.php/v1/search/page?q=&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>encodedQuery<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&amp;limit=5&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>HttpRequest<span style="color:#bbb"> </span>request<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>HttpRequest.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">uri</span>(URI.<span style="color:#4070a0">create</span>(url))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">header</span>(<span style="color:#4070a0">&#34;User-Agent&#34;</span>,<span style="color:#bbb"> </span>USER_AGENT)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">GET</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>HttpResponse<span style="color:#666">&lt;</span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>httpClient.<span style="color:#4070a0">send</span>(request,<span style="color:#bbb"> </span>HttpResponse.<span style="color:#4070a0">BodyHandlers</span>.<span style="color:#4070a0">ofString</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(response.<span style="color:#4070a0">statusCode</span>()<span style="color:#bbb"> </span><span style="color:#666">!=</span><span style="color:#bbb"> </span>200)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Error searching Wikipedia: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>response.<span style="color:#4070a0">statusCode</span>()<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34; - &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>response.<span style="color:#4070a0">body</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>JsonNode<span style="color:#bbb"> </span>rootNode<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>objectMapper.<span style="color:#4070a0">readTree</span>(response.<span style="color:#4070a0">body</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>JsonNode<span style="color:#bbb"> </span>pagesNode<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>rootNode.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;pages&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>StringBuilder<span style="color:#bbb"> </span>result<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>StringBuilder(<span style="color:#4070a0">&#34;Search Results:\n\n&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(pagesNode<span style="color:#bbb"> </span><span style="color:#666">!=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span><span style="color:#666">&amp;&amp;</span><span style="color:#bbb"> </span>pagesNode.<span style="color:#4070a0">isArray</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(JsonNode<span style="color:#bbb"> </span>page<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>pagesNode)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>String<span style="color:#bbb"> </span>title<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>page.<span style="color:#4070a0">has</span>(<span style="color:#4070a0">&#34;title&#34;</span>)<span style="color:#bbb"> </span><span style="color:#666">?</span><span style="color:#bbb"> </span>page.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;title&#34;</span>).<span style="color:#4070a0">asText</span>()<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>String<span style="color:#bbb"> </span>description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>page.<span style="color:#4070a0">has</span>(<span style="color:#4070a0">&#34;description&#34;</span>)<span style="color:#bbb"> </span><span style="color:#666">?</span><span style="color:#bbb"> </span>page.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;description&#34;</span>).<span style="color:#4070a0">asText</span>()<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;No description&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>result.<span style="color:#4070a0">append</span>(<span style="color:#4070a0">&#34;- **&#34;</span>).<span style="color:#4070a0">append</span>(title).<span style="color:#4070a0">append</span>(<span style="color:#4070a0">&#34;**: &#34;</span>).<span style="color:#4070a0">append</span>(description).<span style="color:#4070a0">append</span>(<span style="color:#4070a0">&#34;\n&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(result.<span style="color:#4070a0">length</span>()<span style="color:#bbb"> </span><span style="color:#666">==</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Search Results:\n\n&#34;</span>.<span style="color:#4070a0">length</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;No results found for query: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>query;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>result.<span style="color:#4070a0">toString</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@Tool</span>(<span style="color:#4070a0">&#34;Retrieve the content of a specific Wikipedia page by its exact title, converted to Markdown format. Use the exact title returned by searchWikipedia.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">getWikipediaPageContent</span>(String<span style="color:#bbb"> </span>title)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">throws</span><span style="color:#bbb"> </span>Exception<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Retrieving Wikipedia page: {}&#34;</span>,<span style="color:#bbb"> </span>title);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>String<span style="color:#bbb"> </span>encodedTitle<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>URLEncoder.<span style="color:#4070a0">encode</span>(title.<span style="color:#4070a0">replace</span>(<span style="color:#4070a0">&#34; &#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;_&#34;</span>),<span style="color:#bbb"> </span>StandardCharsets.<span style="color:#4070a0">UTF_8</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>String<span style="color:#bbb"> </span>url<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;https://en.wikipedia.org/api/rest_v1/page/html/&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>encodedTitle;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>HttpRequest<span style="color:#bbb"> </span>request<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>HttpRequest.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">uri</span>(URI.<span style="color:#4070a0">create</span>(url))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">header</span>(<span style="color:#4070a0">&#34;User-Agent&#34;</span>,<span style="color:#bbb"> </span>USER_AGENT)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">GET</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>HttpResponse<span style="color:#666">&lt;</span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>httpClient.<span style="color:#4070a0">send</span>(request,<span style="color:#bbb"> </span>HttpResponse.<span style="color:#4070a0">BodyHandlers</span>.<span style="color:#4070a0">ofString</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(response.<span style="color:#4070a0">statusCode</span>()<span style="color:#bbb"> </span><span style="color:#666">!=</span><span style="color:#bbb"> </span>200)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Error retrieving Wikipedia page: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>response.<span style="color:#4070a0">statusCode</span>()<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34; - &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>response.<span style="color:#4070a0">body</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>String<span style="color:#bbb"> </span>htmlContent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>response.<span style="color:#4070a0">body</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Converting HTML to Markdown for page: {}&#34;</span>,<span style="color:#bbb"> </span>title);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>copyDown.<span style="color:#4070a0">convert</span>(htmlContent);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>I didn&rsquo;t even touch the code at all, and it worked flawlessly out of the box.
I would probably only update the version dependency on LangChain4j which is not up-to-date.
But that&rsquo;s about it.</p>
<h2 id="configuring-the-mcp-server-in-gemini-cli">Configuring the MCP Server in Gemini CLI</h2>
<p>To use this server, you just need to register it in your <code>~/.gemini/settings.json</code>. Gemini CLI will then automatically launch it as a child process and communicate with it over Standard Input/Output.</p>
<p>Add the following to your <code>mcpServers</code> section:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;mcpServers&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;wikipedia-mcp&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;command&#34;</span>: <span style="color:#4070a0">&#34;jbang&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;args&#34;</span>: [
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#34;run&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#34;--quiet&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#34;/path/to/your/WikipediaMcpServer.java&#34;</span>
</span></span><span style="display:flex;"><span>      ]
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Once the MCP configuration is saved and Gemini CLI reloaded, I could check that the MCP server was available,
by running the <code>/mcp list</code> command:</p>
<p><figure>
  <a href="#img-dcbe7dff43048b8dfc4bcac68710839f">
    <img src="/img/gemini-cli/wikipedia-mcp/gcli-mcp-wikipedia-5.jpg"
      alt="List of available MCP servers tools in Gemini CLI"
       />
  </a>
  <figcaption>List of available MCP servers tools in Gemini CLI</figcaption>
</figure>
<div class="lightbox" id="img-dcbe7dff43048b8dfc4bcac68710839f">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/wikipedia-mcp/gcli-mcp-wikipedia-5.jpg"
    alt="List of available MCP servers tools in Gemini CLI"
     />
  <div class="lightbox-caption">List of available MCP servers tools in Gemini CLI</div>
</div>
</p>
<p>There are 2 tools available: one for fetching a list of relevant pages, and the other to fetch the content of an individual page.</p>
<h2 id="putting-it-up-to-the-test">Putting it up to the test</h2>
<p>Yesterday, the &#x1f680; Artemis 2 mission launched to travel around the moon &#x1f314;
I&rsquo;m sure Wikipedia is already updated with the latest information about the status of the mission.
Let&rsquo;s double check:</p>
<p><figure>
  <a href="#img-4cefb0436db29b82956dd3ed8f364f6b">
    <img src="/img/gemini-cli/wikipedia-mcp/gcli-mcp-wikipedia-6.jpg"
      alt="Asking about the Artemis 2 mission"
       />
  </a>
  <figcaption>Asking about the Artemis 2 mission</figcaption>
</figure>
<div class="lightbox" id="img-4cefb0436db29b82956dd3ed8f364f6b">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/wikipedia-mcp/gcli-mcp-wikipedia-6.jpg"
    alt="Asking about the Artemis 2 mission"
     />
  <div class="lightbox-caption">Asking about the Artemis 2 mission</div>
</div>
</p>
<p>It found relevant pages, then it loaded the content of those pages and found the information,
and summarized its findings:</p>
<p><figure>
  <a href="#img-27a4f68768636f19013c8be8790f8f3b">
    <img src="/img/gemini-cli/wikipedia-mcp/gcli-mcp-wikipedia-7.jpg"
      alt="Response about the status of the Artemis 2 mission"
       />
  </a>
  <figcaption>Response about the status of the Artemis 2 mission</figcaption>
</figure>
<div class="lightbox" id="img-27a4f68768636f19013c8be8790f8f3b">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/wikipedia-mcp/gcli-mcp-wikipedia-7.jpg"
    alt="Response about the status of the Artemis 2 mission"
     />
  <div class="lightbox-caption">Response about the status of the Artemis 2 mission</div>
</div>
</p>
<p>Mission in progress!
But my own personal mission over those few interactive prompts inside Gemini CLI is accomplished:
in less than 5 minutes, I had my custom MCP server to query Wikipedia!
And it took me actually more time to write this article itself!</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>My winning combo of the day: <a href="https://geminicli.com">Gemini CLI</a>, agent skills, Java,
<a href="https://www.jbang.dev/">JBang</a>, and <a href="https://docs.langchain4j.dev">LangChain4j</a>&hellip; and boom &#x1f4a5;</p>
<p>Maybe we&rsquo;ll find that obvious in a few months, but I&rsquo;m still impressed by how it is today, in just a few prompts,
to create something useful like an MCP server that your friendly AI agents can use.
Building tools for your AI agents has never been this fluid.</p>
<p>Happy hacking! 🚀
And let&rsquo;s go to the &#x1f314; and beyond!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Building my Comic Trip agent with ADK Java 1.0</title><link>https://glaforge.dev/posts/2026/03/30/building-my-comic-trip-agent-with-adk-java-1-0/</link><pubDate>Mon, 30 Mar 2026 22:33:09 +0200</pubDate><guid>https://glaforge.dev/posts/2026/03/30/building-my-comic-trip-agent-with-adk-java-1-0/</guid><description>&lt;p>I&amp;rsquo;m happy to echo here the release of &lt;strong>ADK for Java v1.0&lt;/strong>, Google&amp;rsquo;s &lt;a href="https://google.github.io/adk-docs/">Agent Development Kit&lt;/a> framework to build AI agents in Java.
I spent a lot of time on this project. I also wrote the
&lt;a href="https://developers.googleblog.com/announcing-adk-for-java-100-building-the-future-of-ai-agents-in-java/">announcement blog post&lt;/a>
on the Google for Developers blog.
And I&amp;rsquo;ve recorded this &lt;a href="https://www.youtube.com/watch?v=YqABMjSho_M">YouTube video&lt;/a> highlighting some of the new features of the framework,
in which I&amp;rsquo;m demonstrating some of them via an app I built: my &lt;strong>Comic Trip&lt;/strong> agent (pun intended).&lt;/p></description><content:encoded>
<![CDATA[<p>I&rsquo;m happy to echo here the release of <strong>ADK for Java v1.0</strong>, Google&rsquo;s <a href="https://google.github.io/adk-docs/">Agent Development Kit</a> framework to build AI agents in Java.
I spent a lot of time on this project. I also wrote the
<a href="https://developers.googleblog.com/announcing-adk-for-java-100-building-the-future-of-ai-agents-in-java/">announcement blog post</a>
on the Google for Developers blog.
And I&rsquo;ve recorded this <a href="https://www.youtube.com/watch?v=YqABMjSho_M">YouTube video</a> highlighting some of the new features of the framework,
in which I&rsquo;m demonstrating some of them via an app I built: my <strong>Comic Trip</strong> agent (pun intended).</p>
<p><figure>
  <a href="#img-5b957d6b887fa20b57aa403da25b1dee">
    <img src="/img/comic-trip/comic-trip-1.jpg"
      alt="Screenshot of the Comic Trip app"
       />
  </a>
  <figcaption>Screenshot of the Comic Trip app</figcaption>
</figure>
<div class="lightbox" id="img-5b957d6b887fa20b57aa403da25b1dee">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/comic-trip/comic-trip-1.jpg"
    alt="Screenshot of the Comic Trip app"
     />
  <div class="lightbox-caption">Screenshot of the Comic Trip app</div>
</div>
</p>
<p>The <strong>Comic Trip</strong> agent is a fun little application that transforms your travel photography into a vibrant, pop-art comic strip experience.
Beyond the visual style, it also guesses locations (thanks to Gemini) and enriches each &ldquo;panel&rdquo; with nearby points of interest (via Google Maps integration).</p>
<p>For a recap and a demo of what this application is capable of, please check out the YouTube video:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/YqABMjSho_M?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>This project is a showcase for the <strong>Agent Development Kit (ADK) for Java 1.0</strong>,
demonstrating how to build multi-agent systems with:</p>
<ul>
<li><strong>smart models</strong> — Gemini and &#x1f34c; Nano Banana,</li>
<li><strong>ADK tools</strong> — &#x1f50d; Google Search, &#x1f5fa;&#xfe0f; Google Maps,</li>
<li><strong>ADK services</strong> — Google Cloud Storage artifact service, and</li>
<li><strong>cloud-native storage integration</strong> — Google Cloud <a href="https://cloud.google.com/products/firestore">Firestore</a> database.</li>
</ul>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Source code available on GitHub</span>
      </div>
      <div class="admonition-content">
        <p>If you&rsquo;re interested in seeing the source code of this application,
please have a look at the <a href="https://github.com/glaforge/comic-trip-agent">repository on GitHub</a>.
The repository also explains how to build and deploy this application on <a href="https://cloud.google.com/run/">Google Cloud Run</a>.
And of course, you can run it locally on your machine as well.</p>
      </div>
    </div><h2 id="powered-by-adk-for-java-10">Powered by ADK for Java 1.0</h2>
<p>ADK for Java 1.0 makes it easier to build and coordinate AI agents.
In this application, I leverage several key concepts:</p>
<ul>
<li>
<p><strong>App &amp; Plugins</strong>: The entire agent hierarchy is encapsulated within an <code>App</code>.
We use the <code>LoggingPlugin</code> for seamless execution observability and debugging.
Via this <code>App</code> shell, the logging plugin is actually applied to all the sub-agents involved in the multi-agent system.
No need to configure each sub-agent individually.</p>
</li>
<li>
<p><strong>Runners &amp; Sessions</strong>: An <code>InMemoryRunner</code> manages the execution flow, while an <code>InMemorySessionService</code> ensures that the context for each user&rsquo;s trip is isolated and persistent throughout the multi-step process.</p>
</li>
<li>
<p><strong>Specialized Agents</strong>: We utilize a variety of agent types, including</p>
<ul>
<li><code>LlmAgent</code> for LLM-based tasks,</li>
<li><code>SequentialAgent</code> for step-by-step flows, and</li>
<li><code>ParallelAgent</code> for running multiple agents in parallel.</li>
</ul>
</li>
</ul>
<h2 id="multi-agent-architecture">Multi-Agent Architecture</h2>
<p>The core intelligence of the Comic Trip agent is driven by the following agent hierarchy:</p>
<ol>
<li>
<p><strong><code>picture_analyzer_agent</code> (Gemini 3 Flash)</strong>:
The entry point of our flow. It analyzes the uploaded photograph to extract a detailed description and identify the location.
It&rsquo;s impressive to see how good Gemini is at guessing the location of a landmark globally!</p>
</li>
<li>
<p><strong><code>poi_and_comic_flow</code> (Parallel Execution)</strong>:
Once the context is established, two specialized agents run in parallel:</p>
<ul>
<li>
<p><strong><code>comic_illustrator_agent</code> (Gemini 3.1 Flash Image)</strong>: This multimodal agent (also known as &#x1f34c; &ldquo;Nano Banana 2&rdquo;) transforms the original image into a pop-art masterpiece.</p>
</li>
<li>
<p><strong><code>points_of_interest_agent</code> (Gemini 2.5 Flash)</strong>: Equipped with the <strong><code>GoogleMapsTool</code></strong>, it searches for nearby attractions based on the identified location, adding depth to the travel experience.</p>
</li>
</ul>
</li>
</ol>
<p>To configure the Google Maps integration, you just need to add the <code>.tools(new GoogleMapsTool())</code> call to your agent definition:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>LlmAgent<span style="color:#bbb"> </span>poiGoogleMapsAgent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;points_of_interest_agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Given the location in:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {description_and_location}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Please list points of interest (POI)
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        in the area no further than a kilometer away
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        using the `google_maps` tool.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Each POI should have a name and a description.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Don&#39;t mention distances in your response.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        And don&#39;t start with introductory text for the list.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GoogleMapsTool())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">outputKey</span>(OUTPUT_KEY_POINTS_OF_INTEREST)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>The entire process is orchestrated by a <code>SequentialAgent</code> named <code>main_flow</code>, which strictly orders the initial analysis before triggering the parallel phase.</p>
<p><figure>
  <a href="#img-e12e590203cdc56a9cd2faf1f01fee7e">
    <img src="/img/comic-trip/multi-agent-diagram.png"
      alt="Architecture diagram of the various agents and flows"
       />
  </a>
  <figcaption>Architecture diagram of the various agents and flows</figcaption>
</figure>
<div class="lightbox" id="img-e12e590203cdc56a9cd2faf1f01fee7e">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/comic-trip/multi-agent-diagram.png"
    alt="Architecture diagram of the various agents and flows"
     />
  <div class="lightbox-caption">Architecture diagram of the various agents and flows</div>
</div>
</p>

    <div class="admonition tip">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M272 384c9.6-31.9 29.5-59.1 49.2-86.2c0 0 0 0 0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4c0 0 0 0 0 0c19.8 27.1 39.7 54.4 49.2 86.2l160 0zM192 512c44.2 0 80-35.8 80-80l0-16-160 0 0 16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z"/></svg>
        <span>Quick tip</span>
      </div>
      <div class="admonition-content">
        <p>The above diagram was generated by a <a href="https://glaforge.dev/posts/2025/08/01/visualizing-adk-multiagent-systems/">tool</a> that I vibe-coded a while ago,
that takes your multi-agent source code, and creates diagrams to visualize the flow of agents and their different sub-agents.</p>
      </div>
    </div><h2 id="the-visuals-multimedia-generation-and-artifacts">The Visuals: Multimedia Generation and Artifacts</h2>
<p>Generating a high-quality comic panel is just the first step.
ADK 1.0 handles the resulting multimedia artifacts as well.</p>
<p>The <code>comic_illustrator_agent</code> uses an <code>afterModelCallback</code> to intercept the generated image bytes.
These bytes are then persisted using the <strong><code>GcsArtifactService</code></strong>, which automatically handles the upload to a Google Cloud Storage bucket.
This integration ensures that generated media is stored and is easily accessible via public URLs (for the frontend):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>gs://comic-trip-picture-bucket/comic_trip_app/comic_trip_user/{tripId}/{imageId}.png/0
</span></span></code></pre></div><p>The code in the callback goes through the <code>Content</code> and <code>Part</code>s to find parts that contain the generated image (in an <code>inlineData</code> field),
and we save it as an artifact, via the <code>saveArtifact()</code> method of the <code>callbackContext</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>.<span style="color:#4070a0">afterModelCallback</span>((callbackContext,<span style="color:#bbb"> </span>llmResponse)<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Maybe.<span style="color:#4070a0">fromOptional</span>(llmResponse.<span style="color:#4070a0">content</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">flatMap</span>(Content::parts)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">stream</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">flatMap</span>(List::stream)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">filter</span>(part<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>part.<span style="color:#4070a0">inlineData</span>().<span style="color:#4070a0">isPresent</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">findFirst</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">flatMap</span>(part<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>String<span style="color:#bbb"> </span>imageId<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>generateId();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>callbackContext.<span style="color:#4070a0">saveArtifact</span>(imageId<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;.png&#34;</span>,<span style="color:#bbb"> </span>part)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">blockingAwait</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Optional.<span style="color:#4070a0">of</span>(llmResponse.<span style="color:#4070a0">toBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">content</span>(Content.<span style="color:#4070a0">fromParts</span>(Part.<span style="color:#4070a0">fromText</span>(imageId)))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">build</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}))<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="backend-architecture-quarkus--cloud-services">Backend Architecture: Quarkus &amp; Cloud Services</h2>
<p>The application&rsquo;s web backend is built with the <a href="https://quarkus.io/"><strong>Quarkus</strong> framework</a>,
and deployed on <a href="https://cloud.google.com/run"><strong>Google Cloud Run</strong></a>.
It also serves the frontend assets.</p>
<p>When a user uploads a batch of images, the <code>MissionControlResource</code> receives the multipart request.
To ensure maximum throughput, it uses <strong>Java 21 Virtual Threads</strong> to run the <code>ComicTripAnalyzer</code> agent for each image in parallel.</p>

    <div class="admonition warning">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>
        <span>Warning</span>
      </div>
      <div class="admonition-content">
        <p>As you might notice, I didn&rsquo;t use a <code>ParallelAgent</code> here, but let the Quarkus controller handle the parallelization for me.
An ADK parallel agent makes sense when you have a known discreet set of sub-tasks to run in parallel,
but in my case, I don&rsquo;t know in advance how many images I&rsquo;ll receive.
One approach, though, could be to create a custom sub-class of <code>BaseAgent</code> to handle the fan-in / fan-out approach
in the agent graph instead of at the level of the web controller.</p>
      </div>
    </div><p>While the images live in GCS, the trip&rsquo;s metadata and enriched details (descriptions, POIs, and image links) are stored in <strong>Google Cloud Firestore</strong>.
This saving action is also triggered at the web controller level, once all the agents have run through each image to process.</p>
<h2 id="frontend-vibe-coded-with-stitch-and-antigravity">Frontend: Vibe-Coded with Stitch and Antigravity</h2>
<p>The user interface of the Comic Trip agent was designed by <a href="https://stitch.withgoogle.com/"><strong>Google Stitch</strong></a>
and implemented using <a href="https://antigravity.google/"><strong>Antigravity</strong></a>.</p>
<p><figure>
  <a href="#img-6c503a0a71d9422592d1787fcc85726b">
    <img src="/img/comic-trip/stitch-ui.jpg"
      alt="Screenshot of the Stich user interface where the frontend was designed"
       />
  </a>
  <figcaption>Screenshot of the Stich user interface where the frontend was designed</figcaption>
</figure>
<div class="lightbox" id="img-6c503a0a71d9422592d1787fcc85726b">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/comic-trip/stitch-ui.jpg"
    alt="Screenshot of the Stich user interface where the frontend was designed"
     />
  <div class="lightbox-caption">Screenshot of the Stich user interface where the frontend was designed</div>
</div>
</p>
<p>The frontend interacts with the backend via a simple REST API.
Images are sent as <code>multipart/form-data</code>, and the backend returns a comprehensive JSON response, after reading the metadata from Firestore.
The frontend then dynamically renders the &ldquo;Comic Strip&rdquo; view, pulling the comic-styled illustrations directly from the GCS bucket URLs.</p>
<h2 id="whats-next">What&rsquo;s next?</h2>
<p>The Comic Trip app shows that you can build sophisticated, multi-agent tools without having to worry about low-level state or orchestration.
ADK for Java 1.0 handles the plumbing, so you can focus on defining how your agents behave and what tools they use.</p>
<p>If you&rsquo;re ready to start building:</p>
<ul>
<li>Poke around the <a href="https://github.com/glaforge/comic-trip-agent">source code</a> for Comic Trip.</li>
<li>Check out the <a href="https://www.youtube.com/watch?v=YqABMjSho_M">video</a> for a deep dive into ADK 1.0 features.</li>
<li>Read the <a href="https://developers.googleblog.com/announcing-adk-for-java-100-building-the-future-of-ai-agents-in-java/">official announcement</a> for more context.</li>
<li>Grab the <a href="https://github.com/glaforge/adk-java-maven-template">Maven template</a> to bootstrap your own agent.</li>
</ul>
<p>Happy building! &#x1f916;</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Generating music with Lyria 3 and the Gemini Interactions Java SDK</title><link>https://glaforge.dev/posts/2026/03/25/generating-music-with-lyria-3-and-the-gemini-interactions-java-sdk/</link><pubDate>Wed, 25 Mar 2026 23:33:32 +0100</pubDate><guid>https://glaforge.dev/posts/2026/03/25/generating-music-with-lyria-3-and-the-gemini-interactions-java-sdk/</guid><description>&lt;p>Generative AI isn&amp;rsquo;t just about text or images
(with &lt;a href="https://glaforge.dev/tags/nano-banana/">Nano Banana&lt;/a>) but it&amp;rsquo;s also great at generating videos
(with &lt;a href="https://glaforge.dev/posts/2025/09/10/generating-videos-in-java-with-veo3/">Veo 3&lt;/a>).
And now with the recently &lt;a href="https://blog.google/innovation-and-ai/technology/developers-tools/lyria-3-developers/">released&lt;/a>
&lt;strong>Lyria 3&lt;/strong> model from DeepMind, you can create some engaging and creative music with lyrics (generated, or your own)
or invent a calming instrumental track to loop in the background of your online TikTok or YouTube Shorts.&lt;/p>
&lt;p>And of course, if you&amp;rsquo;re a Java developer like me, &lt;strong>you can do all that in Java&lt;/strong>!&lt;/p></description><content:encoded>
<![CDATA[<p>Generative AI isn&rsquo;t just about text or images
(with <a href="https://glaforge.dev/tags/nano-banana/">Nano Banana</a>) but it&rsquo;s also great at generating videos
(with <a href="https://glaforge.dev/posts/2025/09/10/generating-videos-in-java-with-veo3/">Veo 3</a>).
And now with the recently <a href="https://blog.google/innovation-and-ai/technology/developers-tools/lyria-3-developers/">released</a>
<strong>Lyria 3</strong> model from DeepMind, you can create some engaging and creative music with lyrics (generated, or your own)
or invent a calming instrumental track to loop in the background of your online TikTok or YouTube Shorts.</p>
<p>And of course, if you&rsquo;re a Java developer like me, <strong>you can do all that in Java</strong>!</p>
<p>In this article, we&rsquo;ll learn how to create our own songs and clips with the <strong>Lyria 3</strong> model, in Java,
using my <a href="https://github.com/glaforge/gemini-interactions-api-sdk">Gemini Interactions API Java SDK</a>.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition idea">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M272 384c9.6-31.9 29.5-59.1 49.2-86.2c0 0 0 0 0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4c0 0 0 0 0 0c19.8 27.1 39.7 54.4 49.2 86.2l160 0zM192 512c44.2 0 80-35.8 80-80l0-16-160 0 0 16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z"/></svg>
        <span>Idea</span>
      </div>
      <div class="admonition-content">
        <p>The examples in this article are inspired by this
<a href="https://colab.research.google.com/github/google-gemini/cookbook/blob/main/quickstarts/Get_started_Lyria.ipynb">Colab Notebook</a>
in Python. Don&rsquo;t hesitate to check it out.
And if you want to learn more, this <a href="https://blog.google/innovation-and-ai/technology/developers-tools/lyria-3-developers/">article</a>
shows some fun app integration ideas, like an alarm clock waking you up with a different song every morning!</p>
      </div>
    </div>
    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Info</span>
      </div>
      <div class="admonition-content">
        <p>To run those examples, you&rsquo;ll need to <a href="https://aistudio.google.com/api-keys">get a Gemini API key</a>
in <strong>Google AI Studio</strong>.
You&rsquo;ll then be able to instantiate a Gemini Interactions API client as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>GeminiInteractionsClient<span style="color:#bbb"> </span>client<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GeminiInteractionsClient.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">       </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">       </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div>
      </div>
    </div><h2 id="clip-vs-pro-the-models">Clip vs. Pro: The Models</h2>
<p>Lyria 3 comes in two primary flavors:</p>
<ul>
<li><strong><code>lyria-3-clip-preview</code></strong> — Perfect for generating short clips (<strong>30 second</strong> long), snippets, or quick iterations for sound effects, choruses, and jingles.</li>
<li><strong><code>lyria-3-pro-preview</code></strong> — Fully capable of generating long, structurally cohesive, full-length songs (up to <strong>3 minutes</strong>).</li>
</ul>
<p>Here&rsquo;s an example of firing off a request for a full-length song using the SDK:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ModelInteractionParams<span style="color:#bbb"> </span>request<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ModelInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;models/lyria-3-pro-preview&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">input</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Write a full length epic power metal song
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        about a brave knight fighting a dragon.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        It should have a guitar solo.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseModalities</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Interaction.<span style="color:#4070a0">Modality</span>.<span style="color:#4070a0">AUDIO</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Interaction.<span style="color:#4070a0">Modality</span>.<span style="color:#4070a0">TEXT</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Interaction<span style="color:#bbb"> </span>interaction<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">create</span>(request);<span style="color:#bbb">
</span></span></span></code></pre></div><figure class="audio">
<audio controls preload="metadata"  >
<source src="/mp3/lyria-full-knight-dragon.mp3" type="audio/mpeg">
Your browser does not support the audio element.
</audio>
</figure>
<p>In addition to the MP3, Lyria also generated the following lyrics:

<details>
  <summary>Click to read the full lyrics</summary>
  <pre tabindex="0"><code>[[A0]]
[[B1]]
[16.0:] Cold mountain peaks in the morning haze,
[:] The knight rides forth through the silver maze,
[:] With steel in hand and a heart of fire,
[:] To face the beast and the burning pyre.
[:] Through ancient gates where the shadows sleep,
[:] He finds the path to the valley deep,
[:] No fear of death in his iron soul,
[:] He seeks the fire and the final goal.
[[C2]]
[48.0:] Upon the wind comes the dragon’s breath!
[:] A storm of flame and a dance of death!
[:] Oh, carry the flame on the steel of the knight!
[:] Into the dragon, into the light!
[:] The world will tremble as titans collide!
[:] Nowhere for the ancient beast to hide!
[:] Into the light!
[[B3]]
[80.0:] The claws of iron and teeth of obsidian,
[:] A mountain of scales in the dark stygian,
[:] The sword strikes home but the sparks do fly,
[:] Underneath the heavy sulfur sky.
[:] A roar that echoes through the mountain hall,
[:] The knight stands steady, he will not fall,
[:] Through smoke and cinders the legend grows,
[:] He strikes the heart where the furnace glows!
[[C4]]
[112.0:] Upon the wind comes the dragon’s breath!
[:] A storm of flame and a dance of death!
[:] Oh, carry the flame on the steel of the knight!
[:] Into the dragon, into the light!
[:] The world will tremble as titans collide!
[:] Nowhere for the ancient beast to hide!
[:] Into the light!
[[A5]]
[[B6]]
[160.0:] The wings are broken, the fire is out,
[:] The knight is standing amidst the doubt,
[:] A savior&#39;s light in the dark of the cave,
[:] To the halls of legend, he’s wise and brave.
[[D7]]
[176.0:] The legend remains. Forevermore!
[:] (Forevermore!)
</code></pre></details>
</p>
<h2 id="mp3-decoding-and-dual-modalities">MP3 Decoding and Dual Modalities</h2>
<p>One of the coolest features of the Interactions API is the ability to request multiple <strong>Response Modalities</strong>.
Notice the <code>responseModalities</code> parameter in the code snippet above?
By requesting both <code>AUDIO</code> and <code>TEXT</code>, the API will return:</p>
<ol>
<li><strong>Text</strong>: The actual lyrics generated and the structural breakdown of the song.</li>
<li><strong>Audio</strong>: The music itself natively encoded as an <strong>MP3 file</strong>.</li>
</ol>
<p>Because of the API&rsquo;s MP3 formatting return type, you don&rsquo;t need to do any complex WAV header manipulation or PCM decoding.
You can safely extract the returned bytes from the payload and push them directly onto disks as an <code>.mp3</code> file:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>interaction.<span style="color:#4070a0">outputs</span>().<span style="color:#4070a0">stream</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">filter</span>(output<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>output<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">instanceof</span><span style="color:#bbb"> </span>AudioContent)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">map</span>(output<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>(AudioContent)<span style="color:#bbb"> </span>output)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">findFirst</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">ifPresent</span>(audio<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Files.<span style="color:#4070a0">write</span>(Paths.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;song.mp3&#34;</span>),<span style="color:#bbb"> </span>audio.<span style="color:#4070a0">data</span>()));<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="prompt-constraints">Prompt Constraints</h2>
<p>The Lyria 3 model is highly receptive to prompting constraints.
Depending on what you pass into the text input, here are four major ways to dictate the song output:</p>
<h3 id="1-give-it-lyrics">1. Give it Lyrics</h3>
<p>You don&rsquo;t have to rely on the model inventing lyrics.
If you have written your own song, you can literally paste the lyrics into the prompt:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ModelInteractionParams<span style="color:#bbb"> </span>request<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ModelInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;models/lyria-3-clip-preview&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">input</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        An uplifting song with guitar riffs about nano banana.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        The lyrics should be:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          Yellow peel, a tiny sweet,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          The Nano Banana, a tropical treat.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          But wait—it hums, it starts to create,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          Switching into AI mode...
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseModalities</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Interaction.<span style="color:#4070a0">Modality</span>.<span style="color:#4070a0">AUDIO</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Interaction.<span style="color:#4070a0">Modality</span>.<span style="color:#4070a0">TEXT</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Interaction<span style="color:#bbb"> </span>interaction<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">create</span>(request);<span style="color:#bbb">
</span></span></span></code></pre></div><figure class="audio">
<audio controls preload="metadata"  >
<source src="/mp3/lyria-lyrics-banana.mp3" type="audio/mpeg">
Your browser does not support the audio element.
</audio>
</figure>
<h3 id="2-control-the-structure">2. Control the Structure</h3>
<p>You can instruct the model on song composition layout by using bracketed metadata
such as <code>[Intro]</code>, <code>[Verse]</code>, <code>[Chorus]</code>, and <code>[Outro]</code>.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ModelInteractionParams<span style="color:#bbb"> </span>request<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ModelInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;models/lyria-3-clip-preview&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">input</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        [Intro] Calm piano music setting a sunset scene on the beach
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        [Verse] Epic rock ballad as the storm rages.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        [Outro] Opera with choir as the sun reappears
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                again through the black clouds.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseModalities</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Interaction.<span style="color:#4070a0">Modality</span>.<span style="color:#4070a0">AUDIO</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Interaction.<span style="color:#4070a0">Modality</span>.<span style="color:#4070a0">TEXT</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Interaction<span style="color:#bbb"> </span>interaction<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">create</span>(request);<span style="color:#bbb">
</span></span></span></code></pre></div><figure class="audio">
<audio controls preload="metadata"  >
<source src="/mp3/lyria-structured-storm.mp3" type="audio/mpeg">
Your browser does not support the audio element.
</audio>
</figure>
<h3 id="3-instrumental-only">3. Instrumental Only</h3>
<p>If you aren&rsquo;t looking for lyrics or vocals, simply instruct the model that the track should be instrumental.
It excels at generating ambient background loops!</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ModelInteractionParams<span style="color:#bbb"> </span>request<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ModelInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;models/lyria-3-clip-preview&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">input</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Create a looping meditation music that feels like the wind.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Instrumental only.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseModalities</span>(Interaction.<span style="color:#4070a0">Modality</span>.<span style="color:#4070a0">AUDIO</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Interaction<span style="color:#bbb"> </span>interaction<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">create</span>(request);<span style="color:#bbb">
</span></span></span></code></pre></div><figure class="audio">
<audio controls preload="metadata"  >
<source src="/mp3/lyria-instrumental-wind.mp3" type="audio/mpeg">
Your browser does not support the audio element.
</audio>
</figure>
<h3 id="4-give-it-a-picture-for-inspiration">4. Give it a Picture for Inspiration!</h3>
<p>Since Lyria 3 is a <strong>multimodal model</strong>, not only can it accept a prompt in input,
but you can also pass images to drive its generative inspiration:</p>
<p><figure>
  <a href="#img-8fb1ac3676e6ab7c2826694804db3fee">
    <img src="https://storage.googleapis.com/generativeai-downloads/images/groceries.jpeg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-8fb1ac3676e6ab7c2826694804db3fee">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://storage.googleapis.com/generativeai-downloads/images/groceries.jpeg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// picture of a groceries list which will drive the lyrics</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#902000">byte</span><span style="color:#666">[]</span><span style="color:#bbb"> </span>imageBytes<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>URI.<span style="color:#4070a0">create</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;https://storage.googleapis.com/generativeai-downloads/images/groceries.jpeg&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">toURL</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">openStream</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">readAllBytes</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ModelInteractionParams<span style="color:#bbb"> </span>request<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ModelInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;models/lyria-3-clip-preview&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">input</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>TextContent(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            An epic song with opera voices about this quest.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Deep synths and a speeding up tempo.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;&#34;&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ImageContent(imageBytes,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;image/jpeg&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseModalities</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Interaction.<span style="color:#4070a0">Modality</span>.<span style="color:#4070a0">AUDIO</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Interaction.<span style="color:#4070a0">Modality</span>.<span style="color:#4070a0">TEXT</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Interaction<span style="color:#bbb"> </span>interaction<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">create</span>(request);<span style="color:#bbb">
</span></span></span></code></pre></div><figure class="audio">
<audio controls preload="metadata"  >
<source src="/mp3/lyria-image-groceries.mp3" type="audio/mpeg">
Your browser does not support the audio element.
</audio>
</figure>
<h2 id="wrap-up">Wrap Up</h2>
<p>Adding music generation to Java applications or AI agents is easier than ever with the Interactions API and Lyria 3.
I highly recommend taking a look at the newly added
<a href="https://github.com/glaforge/gemini-interactions-api-sdk?tab=readme-ov-file#lyria-music-generation">test cases</a>
over in the <code>LyriaTest.java</code> class within the SDK repository to see the full setup in action.</p>
<p>Happy prompting &amp; rocking! 🎸</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Extracting JSON from LLM chatter with JsonSpotter</title><link>https://glaforge.dev/posts/2026/03/22/extracting-json-from-llm-chatter-with-jsonspotter/</link><pubDate>Sun, 22 Mar 2026 17:53:56 +0100</pubDate><guid>https://glaforge.dev/posts/2026/03/22/extracting-json-from-llm-chatter-with-jsonspotter/</guid><description>&lt;p>LLMs are great at generating structured data, in particularly those which support a strict &lt;em>JSON output mode&lt;/em> (sometimes also called &lt;em>structured decoding&lt;/em>), but sometimes they give you a bit more than the requested JSON. You get a Markdown code block wrapped in &lt;em>&amp;ldquo;Here&amp;rsquo;s the data you asked for:&amp;rdquo;&lt;/em> and &lt;em>&amp;ldquo;Hope this helps!&amp;rdquo;&lt;/em>. If you&amp;rsquo;re lucky, the JSON is valid. If you&amp;rsquo;re not, it has trailing commas or comments that break standard parsers.&lt;/p></description><content:encoded>
<![CDATA[<p>LLMs are great at generating structured data, in particularly those which support a strict <em>JSON output mode</em> (sometimes also called <em>structured decoding</em>), but sometimes they give you a bit more than the requested JSON. You get a Markdown code block wrapped in <em>&ldquo;Here&rsquo;s the data you asked for:&rdquo;</em> and  <em>&ldquo;Hope this helps!&rdquo;</em>. If you&rsquo;re lucky, the JSON is valid. If you&rsquo;re not, it has trailing commas or comments that break standard parsers.</p>
<p>I wrote <a href="https://github.com/glaforge/jsonspotter"><strong>JsonSpotter</strong></a> to handle this. It’s a small <strong>Java</strong> library that finds and extracts JSON-like structures from any text, even if the JSON itself is a bit messy.
Then, you can use a lenient JSON parser like <a href="https://github.com/FasterXML/jackson">Jackson 3</a> to parse the extracted JSON to <strong>work with proper type-safe Java objects</strong>, instead of text or maps of lists of maps of&hellip; more basic types.</p>
<h2 id="why-not-just-use-regex">Why not just use Regex?</h2>
<p>You could try <code>indexOf(&quot;{&quot;)</code> or a regular expression, but those break quickly. Nested objects, extra braces in the conversational text, or complex arrays make string manipulation a nightmare.</p>
<p><code>JsonSpotter</code> uses a recursive descent parser. It actually understands the structure it&rsquo;s looking for. It doesn&rsquo;t just find brackets; it validates the object or array boundaries structurally as it scans. By doing so, it can accurately find the <strong>longest well-balanced JSON-like structure</strong> in the text, ensuring that nested objects are handled correctly and that it doesn&rsquo;t get tripped up by random braces in the conversational text.</p>
<h2 id="handling-lenient-json">Handling &ldquo;lenient&rdquo; JSON</h2>
<p>Sometimes LLMs output what I call &ldquo;human-friendly&rdquo; JSON. They don&rsquo;t always output strict and valid JSON. They can add comments to explain fields, add an ellipsis to omit parts of the content, or leave trailing commas. By default, standard libraries like Jackson or Gson will throw an error immediately when they see a <code>// comment</code> or a trailing <code>,</code>.</p>
<p><code>JsonSpotter</code> is built to be <strong>lenient</strong> during extraction. It recognizes:</p>
<ul>
<li>Single and multi-line comments (<code>//</code>, <code>/* */</code>, <code>#</code>)</li>
<li>Unquoted keys and single-quoted strings</li>
<li>Trailing commas</li>
<li>Non-standard numbers like <code>.5</code> or <code>NaN</code></li>
</ul>
<p>Once <code>JsonSpotter</code> extracts the raw string, you can pass it to a proper JSON parser configured for <em>leniency</em> (i.e. being tolerant to malformed JSON content).</p>
<h2 id="a-quick-example">A quick example</h2>
<p>First, extract the JSON from your LLM response:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>String<span style="color:#bbb"> </span>rawText<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;...&#34;</span>;<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// Text returned by your LLM</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>jsonString<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>JsonSpotter.<span style="color:#4070a0">extractJson</span>(rawText);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Then, parse it with something like Jackson 3 (which has great support for lenient features):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">tools.jackson.databind.json.JsonMapper</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">tools.jackson.core.json.JsonReadFeature</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>JsonMapper<span style="color:#bbb"> </span>mapper<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>JsonMapper.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">enable</span>(JsonReadFeature.<span style="color:#4070a0">ALLOW_JAVA_COMMENTS</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">enable</span>(JsonReadFeature.<span style="color:#4070a0">ALLOW_TRAILING_COMMA</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">enable</span>(JsonReadFeature.<span style="color:#4070a0">ALLOW_SINGLE_QUOTES</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">enable</span>(JsonReadFeature.<span style="color:#4070a0">ALLOW_UNQUOTED_PROPERTY_NAMES</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>MyClass<span style="color:#bbb"> </span>myObj<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">  </span>mapper.<span style="color:#4070a0">readValue</span>(jsonString,<span style="color:#bbb"> </span>MyClass.<span style="color:#4070a0">class</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// or</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>JsonNode<span style="color:#bbb"> </span>node<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>mapper.<span style="color:#4070a0">readTree</span>(jsonString);<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="get-it">Get it</h2>
<p><code>JsonSpotter</code> is dependency-free and available on Maven Central.</p>
<p>Add the dependency to your <code>pom.xml</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>io.github.glaforge.jsonspotter<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>jsonspotter<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>0.1.2<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div><p>Or your <code>build.gradle</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>dependencies <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    implementation<span style="color:#666">(</span><span style="color:#4070a0">&#34;io.github.glaforge.jsonspotter:jsonspotter:0.1.2&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Give it a try and let me know what you think on <a href="https://github.com/glaforge/jsonspotter">GitHub</a> if you find it useful.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Fixing AI Slop with a Skill in Gemini CLI</title><link>https://glaforge.dev/posts/2026/03/08/fixing-ai-slop-with-a-skill-in-gemini-cli/</link><pubDate>Sun, 08 Mar 2026 21:29:38 +0100</pubDate><guid>https://glaforge.dev/posts/2026/03/08/fixing-ai-slop-with-a-skill-in-gemini-cli/</guid><description>&lt;p>We all recognize AI writing when we see it. As language models get used for everything, their specific tics are everywhere.
People call this &lt;strong>&amp;ldquo;AI slop&amp;rdquo;&lt;/strong>. The grammar is fine, but the text is boring, repetitive, and lacks any real voice.&lt;/p>
&lt;p>A site called &lt;a href="https://tropes.fyi/">tropes.fyi&lt;/a> tracks these patterns. It lists the exact words and structures that give AI away.
You&amp;rsquo;ll see callouts for overused adverbs like &lt;em>&amp;ldquo;deeply&amp;rdquo;&lt;/em> and &lt;em>&amp;ldquo;arguably,&amp;rdquo;&lt;/em> the dreaded &lt;em>&amp;ldquo;delve&amp;rdquo;&lt;/em> family, and structural crutches like &lt;em>&amp;ldquo;It&amp;rsquo;s not X, it&amp;rsquo;s Y.&amp;rdquo;&lt;/em>&lt;/p></description><content:encoded>
<![CDATA[<p>We all recognize AI writing when we see it. As language models get used for everything, their specific tics are everywhere.
People call this <strong>&ldquo;AI slop&rdquo;</strong>. The grammar is fine, but the text is boring, repetitive, and lacks any real voice.</p>
<p>A site called <a href="https://tropes.fyi/">tropes.fyi</a> tracks these patterns. It lists the exact words and structures that give AI away.
You&rsquo;ll see callouts for overused adverbs like <em>&ldquo;deeply&rdquo;</em> and <em>&ldquo;arguably,&rdquo;</em> the dreaded <em>&ldquo;delve&rdquo;</em> family, and structural crutches like <em>&ldquo;It&rsquo;s not X, it&rsquo;s Y.&rdquo;</em></p>
<p>I wanted a way to automatically fix these issues in my own generated text.
So, I built a <code>deslopify</code> skill for <a href="https://geminicli.com/">Gemini CLI</a>.</p>
<h2 id="how-i-built-it">How I built it</h2>
<p>Gemini CLI has a built-in <a href="https://geminicli.com/docs/cli/creating-skills/"><code>skill-creator</code></a> that handles the boilerplate.
I just asked the CLI to create a new skill, and it generated the folder structure and a <code>SKILL.md</code> file.
It took as reference the Markdown file shared by <a href="https://tropes.fyi/">tropes.fyi</a> with all the typical elements of AI sloppiness.</p>
<p>The <code>SKILL.md</code> file tells the agent how to behave.
I wrote a short workflow instructing the agent to read the user&rsquo;s text, cross-reference it with the anti-patterns from <a href="https://tropes.fyi/">tropes.fyi</a>, and rewrite it.
The goal is just to make the text sound like a normal person wrote it.</p>
<p>Here&rsquo;s what the skill generated by Gemini CLI looks like:</p>
<p><figure>
  <a href="#img-2569003cacf359aaaf2dfc52174a5c0d">
    <img src="/img/gemini-cli/deslopify-skill.jpg"
      alt="deslopify generated skill"
       />
  </a>
  <figcaption>deslopify generated skill</figcaption>
</figure>
<div class="lightbox" id="img-2569003cacf359aaaf2dfc52174a5c0d">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/deslopify-skill.jpg"
    alt="deslopify generated skill"
     />
  <div class="lightbox-caption">deslopify generated skill</div>
</div>
</p>
<p>The structure of the skill is just this <code>SKILL.md</code> file, and a <code>references/</code> directory containing the style guide:</p>
<pre tabindex="0"><code>deslopify/
├── SKILL.md
└── references/
      └── style_guide.md
</code></pre><p>I didn&rsquo;t even have to modify the generated <code>SKILL.md</code> as it did the job perfectly, and was clear out of the box.</p>
<h2 id="trying-it-out">Trying it out</h2>
<p>The code is up on GitHub: <a href="https://github.com/glaforge/deslopify">glaforge/deslopify</a>.</p>
<p>You can install it directly from the repo in Gemini CLI:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gemini skills install https://github.com/glaforge/deslopify
</span></span></code></pre></div><p><figure>
  <a href="#img-f9bd144ff7fb97c7f65cf7bc41d4670d">
    <img src="/img/gemini-cli/install-deslopify-cli-skill.jpg"
      alt="Installing the deslopify skill within Gemini CLI"
       />
  </a>
  <figcaption>Installing the deslopify skill within Gemini CLI</figcaption>
</figure>
<div class="lightbox" id="img-f9bd144ff7fb97c7f65cf7bc41d4670d">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/install-deslopify-cli-skill.jpg"
    alt="Installing the deslopify skill within Gemini CLI"
     />
  <div class="lightbox-caption">Installing the deslopify skill within Gemini CLI</div>
</div>
</p>
<p>Then reload your interactive session:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>/skills reload
</span></span></code></pre></div>
            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Information</span>
      </div>
      <div class="admonition-content">
        <p>Because the skill format is just a directory with a <code>SKILL.md</code> file and some assets, you can actually use it in any AI agent that supports the standard.
Be sure to check the syntax and command to install it in your favorite agent.
And feel free to read more about <a href="https://agentskills.io/home">agent skills</a> to understand their structure.</p>
      </div>
    </div><p>Once it&rsquo;s loaded, you can ask Gemini CLI (or your preferred agent tool) to clean up your text:</p>
<ul>
<li><code>&quot;Deslopify this article: [URL]&quot;</code></li>
<li><code>&quot;Naturalize this draft: [paste text]&quot;</code></li>
<li><code>&quot;Remove the AI slop from my README.md file.&quot;</code></li>
</ul>
<p>The agent will strip out the pompous phrasing and weird structural tics, leaving you with something far more readable.</p>

    <div class="admonition question">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM169.8 165.3c7.9-22.3 29.1-37.3 52.8-37.3l58.3 0c34.9 0 63.1 28.3 63.1 63.1c0 22.6-12.1 43.5-31.7 54.8L280 264.4c-.2 13-10.9 23.6-24 23.6c-13.3 0-24-10.7-24-24l0-13.5c0-8.6 4.6-16.5 12.1-20.8l44.3-25.4c4.7-2.7 7.6-7.7 7.6-13.1c0-8.4-6.8-15.1-15.1-15.1l-58.3 0c-3.4 0-6.4 2.1-7.5 5.3l-.4 1.2c-4.4 12.5-18.2 19-30.6 14.6s-19-18.2-14.6-30.6l.4-1.2zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>
        <span>Is this text generated?</span>
      </div>
      <div class="admonition-content">
        <p>If you were wondering, yes, I generated a good chunk of this blog post from my session in Gemini CLI when creating the <code>deslopify</code> skill!
I went through each and every sentence and made some light edits or sometimes added a paragraph or extra clarification sentence.
I also added all the screenshots, added missing links.
But the skill worked, and for quickly sharing details about a little project like this, this skill came in handy to avoid the <em>blank page</em> syndrome.
Hopefully, you won&rsquo;t feel bored reading it!</p>
      </div>
    </div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Easily Build a Local MCP Server in Java with a Skill inside Gemini CLI</title><link>https://glaforge.dev/posts/2026/02/21/easily-build-a-local-mcp-server-in-java-with-a-skill-in-gemini-cli/</link><pubDate>Sat, 21 Feb 2026 22:57:44 +0100</pubDate><guid>https://glaforge.dev/posts/2026/02/21/easily-build-a-local-mcp-server-in-java-with-a-skill-in-gemini-cli/</guid><description>&lt;p>Recently, I&amp;rsquo;ve been exploring the &lt;strong>Model Context Protocol&lt;/strong> (MCP)
and how to easily create custom servers to extend the capabilities of AI assistants like
&lt;a href="https://geminicli.com">Gemini CLI&lt;/a> which I use daily.&lt;/p>
&lt;p>I wanted a way to build these servers &lt;strong>in Java&lt;/strong> without the heavy boilerplate of a traditional Maven or Gradle project,
or with a complex framework.
The solution? Combining &lt;a href="https://jbang.dev">JBang&lt;/a>, &lt;a href="https://docs.langchain4j.dev">LangChain4j&lt;/a>,
and&amp;hellip; &amp;#x1f941;&amp;hellip; a custom &lt;a href="https://geminicli.com/docs/cli/skills/">Gemini CLI skill&lt;/a>!&lt;/p>
&lt;p>In this post, I&amp;rsquo;ll walk you through how I streamlined the creation of MCP STDIO servers,
by &lt;strong>creating an agent &lt;code>SKILL.md&lt;/code>&lt;/strong> to replicate what I had learned in my previous article.&lt;/p></description><content:encoded>
<![CDATA[<p>Recently, I&rsquo;ve been exploring the <strong>Model Context Protocol</strong> (MCP)
and how to easily create custom servers to extend the capabilities of AI assistants like
<a href="https://geminicli.com">Gemini CLI</a> which I use daily.</p>
<p>I wanted a way to build these servers <strong>in Java</strong> without the heavy boilerplate of a traditional Maven or Gradle project,
or with a complex framework.
The solution? Combining <a href="https://jbang.dev">JBang</a>, <a href="https://docs.langchain4j.dev">LangChain4j</a>,
and&hellip; &#x1f941;&hellip; a custom <a href="https://geminicli.com/docs/cli/skills/">Gemini CLI skill</a>!</p>
<p>In this post, I&rsquo;ll walk you through how I streamlined the creation of MCP STDIO servers,
by <strong>creating an agent <code>SKILL.md</code></strong> to replicate what I had learned in my previous article.</p>
<h2 id="the-recap-jbang-and-langchain4j">The Recap: JBang and LangChain4j</h2>
<p>In that
<a href="https://glaforge.dev/posts/2026/02/11/zero-boilerplate-java-stdio-mcp-servers-with-langchain4j-and-jbang/">article</a>
I wrote recently, <strong>JBang</strong> was perfect for <strong>writing and running self-contained Java scripts</strong>.
It automatically handles dependencies and JVM execution, making it perfect for lightweight MCP servers.
No need for a directory structure, for build files, or pre-compilation.</p>
<p><strong>LangChain4j</strong>&rsquo;s recent
<a href="https://github.com/langchain4j/langchain4j/releases/tag/1.11.0">release</a>
provided the <code>langchain4j-community-mcp-server</code>
<a href="https://docs.langchain4j.dev/tutorials/mcp-stdio-server/#start-the-stdio-server">module</a>,
which allows you to create STDIO MCP servers, without the need for a server framework.
By simply annotating a method of a Java class with <code>@Tool</code>, we can expose some useful tool to an LLM.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition warning">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>
        <span>Beware</span>
      </div>
      <div class="admonition-content">
        <p>The critical requirement for an MCP STDIO server is ensuring that JSON-RPC communication over <code>System.out</code> remains uncorrupted.
This means all logging <em>must</em> be redirected to <code>System.err</code>.</p>
      </div>
    </div><p>But to create and install a new MCP server, I had to do a fair bit of copy and paste, and a bit of scaffolding.
That&rsquo;s how I came up with the &#x1f4a1; idea of <strong>creating an agent skill to simplify this task</strong>!</p>
<h2 id="step-1--automating-with-a-skill-for-gemini-cli">Step 1 — Automating with a Skill for Gemini CLI</h2>
<p>Instead of writing the boilerplate manually every time, I first created a custom Gemini CLI skill (<code>jbang-mcp-server.skill</code>).
To do this efficiently, I leveraged Gemini CLI&rsquo;s own <a href="https://geminicli.com/docs/cli/creating-skills/"><code>skill-creator</code> skill</a>,
which is designed to bootstrap new capabilities for the agent.</p>
<p><figure>
  <a href="#img-8d8513216c90f3464cd35eb92863e9c2">
    <img src="/img/mcp/skill/request-skill-creation.jpg"
      alt="Requesting a SKILL.md Creation by Gemini CLI&rsquo;s Skill Creator"
       />
  </a>
  <figcaption>Requesting a SKILL.md Creation by Gemini CLI&rsquo;s Skill Creator</figcaption>
</figure>
<div class="lightbox" id="img-8d8513216c90f3464cd35eb92863e9c2">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/mcp/skill/request-skill-creation.jpg"
    alt="Requesting a SKILL.md Creation by Gemini CLI&rsquo;s Skill Creator"
     />
  <div class="lightbox-caption">Requesting a SKILL.md Creation by Gemini CLI&rsquo;s Skill Creator</div>
</div>
</p>
<p>I fed the skill creator the whole article mentioned above,
and it produced the following <code>SKILL.md</code> file to act as a specialized &ldquo;generator&rdquo;
for automating the repetitive parts of building and installing Java-based MCP servers:</p>
<details>
<summary>Click to view the generated SKILL.md</summary>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>---
</span></span><span style="display:flex;"><span>name: jbang-mcp-server
</span></span><span style="display:flex;"><span>description: Scaffolds and installs zero boilerplate Java-based MCP STDIO servers using JBang and LangChain4j for Gemini CLI. Use this to quickly bootstrap an MCP server from scratch.
</span></span><span style="display:flex;"><span>---
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000080;font-weight:bold"># JBang LangChain4j MCP Server Creator
</span></span></span><span style="display:flex;"><span><span style="color:#000080;font-weight:bold"></span>
</span></span><span style="display:flex;"><span>This skill helps quickly scaffold a new Java-based MCP STDIO server using JBang and LangChain4j, and installs it into Gemini CLI&#39;s <span style="color:#4070a0">`settings.json`</span>.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold">## Process
</span></span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold"></span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">1.</span>  <span style="font-weight:bold">**Ask User for Details:**</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">*</span>   Desired file name (e.g., <span style="color:#4070a0">`McpToolServer.java`</span>) and path to save it.
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">*</span>   The name of the server to register in <span style="color:#4070a0">`~/.gemini/settings.json`</span> (e.g., <span style="color:#4070a0">`java-calc`</span>).
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">*</span>   (Optional) High-level description of the tools they want to add initially.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">2.</span>  <span style="font-weight:bold">**Scaffold the Server:**</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">*</span>   Read the template file located at <span style="color:#4070a0">`assets/McpServerTemplate.java`</span> using the <span style="color:#4070a0">`read_file`</span> tool. Note that the path to <span style="color:#4070a0">`assets/McpServerTemplate.java`</span> needs to be resolved relative to the skill directory or read from the skill&#39;s bundled assets. As an alternative if the absolute path is unknown, directly write out the template contents described below.
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">*</span>   Replace <span style="color:#4070a0">`{SERVER_CLASS_NAME}`</span> with the base name of the requested Java file (e.g., <span style="color:#4070a0">`McpToolServer`</span> if file is <span style="color:#4070a0">`McpToolServer.java`</span>).
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">*</span>   Replace <span style="color:#4070a0">`{TOOL_CLASS_NAME}`</span> with a related name (e.g., <span style="color:#4070a0">`MyTools`</span>).
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">*</span>   (Optional) Modify the <span style="color:#4070a0">`@Tool`</span> annotated methods to reflect the user&#39;s requirements.
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">*</span>   Write the finalized content to the user&#39;s requested path using the <span style="color:#4070a0">`write_file`</span> tool.
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">*</span>   Make the file executable using <span style="color:#4070a0">`chmod +x &lt;path_to_file&gt;`</span> via <span style="color:#4070a0">`run_shell_command`</span>.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">3.</span>  <span style="font-weight:bold">**Verify the Server:**</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">*</span>   Run jbang build &lt;<span style="color:#062873;font-weight:bold">path_to_file</span>&gt; using the run_shell_command tool to check for any compilation errors.
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">*</span>   If there are compilation errors, use the <span style="color:#4070a0">`replace_tool`</span> to fix them.
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">*</span>   Repeat the compilation check until successful.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">4.</span>  <span style="font-weight:bold">**Install the Server in Gemini CLI:**</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">*</span>   Read <span style="color:#4070a0">`~/.gemini/settings.json`</span>.
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">*</span>   Use the <span style="color:#4070a0">`replace`</span> tool or jq via <span style="color:#4070a0">`run_shell_command`</span> to inject a new entry under <span style="color:#4070a0">`mcpServers`</span>.
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">*</span>   The new entry should look like this:
</span></span><span style="display:flex;"><span>        ``<span style="color:#4070a0">`json
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;{server_name}&#34;: {
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          &#34;command&#34;: &#34;jbang&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          &#34;args&#34;: [
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;run&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;--quiet&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;{absolute_path_to_java_file}&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          ]
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        }
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        `</span>`<span style="color:#4070a0">`
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    *   Inform the user that the server has been created and configured, and remind them that Gemini CLI automatically reloads configurations.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">## Template Backup
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">If `</span>assets/McpServerTemplate.java<span style="color:#4070a0">` cannot be read, use this template:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">`</span>``java
</span></span><span style="display:flex;"><span>///usr/bin/env jbang &#34;$0&#34; &#34;$@&#34; ; exit $?
</span></span><span style="display:flex;"><span>//DEPS dev.langchain4j:langchain4j-core:1.11.0
</span></span><span style="display:flex;"><span>//DEPS dev.langchain4j:langchain4j-community-mcp-server:1.11.0-beta19
</span></span><span style="display:flex;"><span>//DEPS org.slf4j:slf4j-simple:2.0.17
</span></span><span style="display:flex;"><span>//JAVA 21
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>import dev.langchain4j.agent.tool.Tool;
</span></span><span style="display:flex;"><span>import dev.langchain4j.community.mcp.server.McpServer;
</span></span><span style="display:flex;"><span>import dev.langchain4j.community.mcp.server.transport.StdioMcpServerTransport;
</span></span><span style="display:flex;"><span>import org.slf4j.Logger;
</span></span><span style="display:flex;"><span>import org.slf4j.LoggerFactory;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>import java.util.List;
</span></span><span style="display:flex;"><span>import java.util.concurrent.CountDownLatch;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>public class {SERVER_CLASS_NAME} {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    static {
</span></span><span style="display:flex;"><span>        // Configure SLF4J Simple Logger to write to System.err
</span></span><span style="display:flex;"><span>        // This is crucial for MCP servers over STDIO to avoid polluting stdout
</span></span><span style="display:flex;"><span>        System.setProperty(&#34;org.slf4j.simpleLogger.logFile&#34;, &#34;System.err&#34;);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    private static final Logger log = LoggerFactory.getLogger({SERVER_CLASS_NAME}.class);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    public static void main(String[] args) throws Exception {
</span></span><span style="display:flex;"><span>        log.info(&#34;Starting LangChain4j MCP Server...&#34;);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        // Instantiate tools
</span></span><span style="display:flex;"><span>        {TOOL_CLASS_NAME} tools = new {TOOL_CLASS_NAME}();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        // Create Server
</span></span><span style="display:flex;"><span>        McpServer server = new McpServer(List.of(tools));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        // Start Transport
</span></span><span style="display:flex;"><span>        StdioMcpServerTransport transport = new StdioMcpServerTransport(server);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        log.info(&#34;MCP Server started successfully on STDIO.&#34;);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        // Keep Alive
</span></span><span style="display:flex;"><span>        new CountDownLatch(1).await();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    // --- Tool Definition ---
</span></span><span style="display:flex;"><span>    public static class {TOOL_CLASS_NAME} {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#d55537;font-weight:bold">@Tool</span>(&#34;Description of your tool&#34;)
</span></span><span style="display:flex;"><span>        public String sampleTool(String input) {
</span></span><span style="display:flex;"><span>            log.info(&#34;Called sampleTool with {}&#34;, input);
</span></span><span style="display:flex;"><span>            return &#34;Processed: &#34; + input;
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="key-rules">Key Rules</h2>
<ul>
<li><strong>Logging:</strong> JBang STDIO servers MUST write all logs to <code>System.err</code> to avoid polluting the JSON-RPC standard output stream. This is already handled in the template via <code>System.setProperty(&quot;org.slf4j.simpleLogger.logFile&quot;, &quot;System.err&quot;);</code> but ensure this is maintained if modifying the file structure.</li>
<li><strong>Dependencies:</strong> The template relies on LangChain4j and slf4j-simple. Do not remove the <code>//DEPS</code> directives at the top of the template.</li>
</ul>
</details>
<p>Along the way, Gemini CLI asked me a few questions, like how to name the skill, the Java class, etc.
And of course, it also asked me for permission to create that skill:</p>
<p><figure>
  <a href="#img-b6e94b6ab8dcaf9de22596584c1cf698">
    <img src="/img/mcp/skill/activate-skill.jpg"
      alt="Gemini CLI asked for Permission to Create a Skill"
       />
  </a>
  <figcaption>Gemini CLI asked for Permission to Create a Skill</figcaption>
</figure>
<div class="lightbox" id="img-b6e94b6ab8dcaf9de22596584c1cf698">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/mcp/skill/activate-skill.jpg"
    alt="Gemini CLI asked for Permission to Create a Skill"
     />
  <div class="lightbox-caption">Gemini CLI asked for Permission to Create a Skill</div>
</div>
</p>
<p>Once installed, I could check that the skill was available in my Gemini CLI session:</p>
<p><figure>
  <a href="#img-cd8744d027eb3e31c005678269681791">
    <img src="/img/mcp/skill/skills-list.jpg"
      alt="Gemini CLI Skills List"
       />
  </a>
  <figcaption>Gemini CLI Skills List</figcaption>
</figure>
<div class="lightbox" id="img-cd8744d027eb3e31c005678269681791">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/mcp/skill/skills-list.jpg"
    alt="Gemini CLI Skills List"
     />
  <div class="lightbox-caption">Gemini CLI Skills List</div>
</div>
</p>
<p>Here is how the skill works and why it&rsquo;s useful:</p>
<h3 id="zero-boilerplate-scaffolding">Zero-Boilerplate Scaffolding</h3>
<p>When triggered, the skill creates a standalone Java file that is immediately ready to run as a script.
It automatically includes:</p>
<ul>
<li><strong>JBang Directives:</strong> <code>//DEPS</code> and <code>//JAVA</code> lines so you don&rsquo;t need a <code>pom.xml</code> or <code>build.gradle</code>.</li>
<li><strong>MCP Server Setup:</strong> The boilerplate code required to initialize the <code>McpServer</code> and connect it to a <code>StdioMcpServerTransport</code>.</li>
<li><strong>Critical Logging Configuration:</strong> It includes a static block that redirects all SLF4J logs to <code>System.err</code>.
This is vital for MCP STDIO servers because logging to <code>System.out</code> would corrupt the JSON-RPC messages used to talk to the AI.</li>
</ul>
<h3 id="automatic-registration">Automatic Registration</h3>
<p>One of the most tedious parts of adding an MCP server is editing the <code>~/.gemini/settings.json</code> file manually.
This skill handles that automatically:</p>
<ul>
<li>It calculates the absolute path to your new Java file.</li>
<li>It injects a new entry into the <code>mcpServers</code> section of your configuration.</li>
<li>It sets up the <code>jbang run --quiet</code> command so Gemini CLI knows exactly how to start your server.</li>
</ul>
<h3 id="rapid-tool-development">Rapid Tool Development</h3>
<p>The skill provides a template with a sample <code>@Tool</code>.
This means you can go from &ldquo;I want a new tool&rdquo; to &ldquo;I have a working tool&rdquo; in seconds
by just naming the server and then having the AI modifying the logic inside the newly generated Java class.</p>
<p>With this skill installed, bootstrapping a new MCP server takes seconds rather than minutes.</p>
<h2 id="step-2--building-the-file-tree-tool">Step 2 — Building the &ldquo;File Tree&rdquo; Tool</h2>
<p>Using our new skill, to take it for a ride, I scaffolded a server named <code>TreeMcpServer.java</code>.
My goal was to create a tool that the LLM could use to inspect the local file system structure.</p>
<p>I let Gemini CLI implement a <code>FileTreeTools</code> class with a <code>tree</code> method.
This method takes a directory path and uses Java&rsquo;s <code>java.nio.file</code> API
to recursively build a string representation of the directory tree
(limiting the depth to prevent massive outputs).</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Tool</span>(<span style="color:#4070a0">&#34;Displays a tree of the local directories and files in the specified path&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">tree</span>(String<span style="color:#bbb"> </span>pathStr)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ... directory traversal logic ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="step-3--troubleshooting-the-build">Step 3 — Troubleshooting the Build</h2>
<p>I hit a small snag during development.
When I first asked Gemini CLI to show the file tree, it couldn&rsquo;t connect to the tool.
This was actually due to a compilation error.</p>
<p>To debug, I asked Gemini CLI to run a compilation check using JBang: <code>jbang build TreeMcpServer.java</code>.
This immediately highlighted the issue:
I had some unclosed string literals in the generated Java code where newline characters were literally inserted instead of escaped.</p>
<p>Using the Gemini CLI&rsquo;s <code>replace</code> tool, I quickly fixed the string literals.
But what was interesting with this issue is that I was able to <strong>ask Gemini CLI to update the <code>SKILL.md</code></strong>
to double check that the generated code compiled properly.
This allowed me to <strong>improve the skill to be more rock-solid</strong>!</p>

    <div class="admonition tip">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M272 384c9.6-31.9 29.5-59.1 49.2-86.2c0 0 0 0 0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4c0 0 0 0 0 0c19.8 27.1 39.7 54.4 49.2 86.2l160 0zM192 512c44.2 0 80-35.8 80-80l0-16-160 0 0 16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z"/></svg>
        <span>Reload skills</span>
      </div>
      <div class="admonition-content">
        <p>In Gemini CLI, if you updated a skill (for example, here, I improved the skill to handle potential compilation errors)
you can request to reload the skill with the following slash command:</p>
<pre tabindex="0"><code>/skills reload
</code></pre>
      </div>
    </div><h2 id="the-result">The Result</h2>
<p>With the compilation issues resolved, the Gemini CLI immediately recognized the newly registered <code>file-tree</code> MCP server.
When asked to &ldquo;Show me a file tree of the current directory,&rdquo; the CLI autonomously invoked our Java tool:</p>
<p><figure>
  <a href="#img-4d8e6a9fead3b3fb9da9fa41daa5231a">
    <img src="/img/mcp/skill/mcp-tool-call.jpg"
      alt="Gemini CLI MCP Tool Call Approval"
       />
  </a>
  <figcaption>Gemini CLI MCP Tool Call Approval</figcaption>
</figure>
<div class="lightbox" id="img-4d8e6a9fead3b3fb9da9fa41daa5231a">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/mcp/skill/mcp-tool-call.jpg"
    alt="Gemini CLI MCP Tool Call Approval"
     />
  <div class="lightbox-caption">Gemini CLI MCP Tool Call Approval</div>
</div>
</p>
<p>&hellip;and returned a clean, formatted representation of the workspace directly in the chat:</p>
<p><figure>
  <a href="#img-1a3c4d69d8eaacb875b0bf3448b39145">
    <img src="/img/mcp/skill/mcp-tool-result.jpg"
      alt="Gemini CLI MCP Tool Result"
       />
  </a>
  <figcaption>Gemini CLI MCP Tool Result</figcaption>
</figure>
<div class="lightbox" id="img-1a3c4d69d8eaacb875b0bf3448b39145">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/mcp/skill/mcp-tool-result.jpg"
    alt="Gemini CLI MCP Tool Result"
     />
  <div class="lightbox-caption">Gemini CLI MCP Tool Result</div>
</div>
</p>
<p>This workflow — using an AI assistant to build a skill,
which in turn builds a tool that extends the assistant itself —
is a powerful demonstration of how quickly we can iterate and expand our development capabilities
using standard Java tools like <a href="https://jbang.dev">JBang</a> and <a href="https://docs.langchain4j.dev">LangChain4j</a>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Agent skills are a powerful way to <strong>automate boring, repetitive work</strong>.
Instead of manually scaffolding boilerplate code every time you want to create a new MCP server (or any other task),
you can delegate that task to an AI agent — and then <strong>automatically package what you learned into a reusable skill</strong>
that you can even share with others.</p>
<p>What&rsquo;s particularly interesting is how the Gemini CLI <strong>agent creation skill</strong> works as a bridge between exploration and automation.
During my interactive session with Gemini CLI, I experimented with building MCP servers, discovered the patterns,
and hit challenges that I solved on the fly.</p>
<p>Rather than keeping that knowledge locked in chat history, the agent creation skill let me capture and summarize
everything I had learned — the best practices, the gotchas, the template structure — into a single, reusable <code>SKILL.md</code> file.</p>
<p>Now, what took me a session of trial-and-error can be replicated instantly by anyone (or by future me) with a single skill invocation.</p>

    <div class="admonition tip">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M272 384c9.6-31.9 29.5-59.1 49.2-86.2c0 0 0 0 0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4c0 0 0 0 0 0c19.8 27.1 39.7 54.4 49.2 86.2l160 0zM192 512c44.2 0 80-35.8 80-80l0-16-160 0 0 16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z"/></svg>
        <span>The Real Power of Agent Skills</span>
      </div>
      <div class="admonition-content">
        <p>They turn ad-hoc experimentation into systematic, shareable automation!</p>
<p>If you want to <strong>learn more about agent skills</strong>, be sure to check
this <a href="https://danicat.dev/posts/agent-skills-gemini-cli/">great article</a> from my colleague Daniela,
who used skills to <em>turn a repetitive task into a more automated workflow</em>.</p>
      </div>
    </div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Zero Boilerplate Java STDIO MCP Servers with LangChain4j and JBang</title><link>https://glaforge.dev/posts/2026/02/11/zero-boilerplate-java-stdio-mcp-servers-with-langchain4j-and-jbang/</link><pubDate>Wed, 11 Feb 2026 10:55:40 +0100</pubDate><guid>https://glaforge.dev/posts/2026/02/11/zero-boilerplate-java-stdio-mcp-servers-with-langchain4j-and-jbang/</guid><description>&lt;p>By now, you&amp;rsquo;re certainly all familiar with the &lt;strong>Model Context Protocol (MCP)&lt;/strong>?
It&amp;rsquo;s the standard for connecting Large Language Models (LLMs) to tools and data.
But if you look at the current ecosystem, you&amp;rsquo;ll see a lot of Python and TypeScript&amp;hellip;&lt;/p>
&lt;p>As a Java developer, you might be wondering:
&lt;em>How can I easily and quickly run my own MCP servers?&lt;/em>&lt;/p>
&lt;p>On this blog, I&amp;rsquo;ve explained how to develop MCP servers
with &lt;a href="https://glaforge.dev/tags/quarkus/">Quarkus&lt;/a> and &lt;a href="https://glaforge.dev/tags/micronaut/">Micronaut&lt;/a>.
But thanks to a recent &lt;a href="https://docs.langchain4j.dev/tutorials/mcp-stdio-server#start-the-stdio-server">community contribution&lt;/a>
to &lt;strong>&lt;a href="https://docs.langchain4j.dev">LangChain4j&lt;/a>&lt;/strong>,
and the simplicity of &lt;strong>&lt;a href="https://jbang.dev">JBang&lt;/a>&lt;/strong>,
building a local MCP server in Java is even easier and with zero boilerplate.&lt;/p></description><content:encoded>
<![CDATA[<p>By now, you&rsquo;re certainly all familiar with the <strong>Model Context Protocol (MCP)</strong>?
It&rsquo;s the standard for connecting Large Language Models (LLMs) to tools and data.
But if you look at the current ecosystem, you&rsquo;ll see a lot of Python and TypeScript&hellip;</p>
<p>As a Java developer, you might be wondering:
<em>How can I easily and quickly run my own MCP servers?</em></p>
<p>On this blog, I&rsquo;ve explained how to develop MCP servers
with <a href="https://glaforge.dev/tags/quarkus/">Quarkus</a> and <a href="https://glaforge.dev/tags/micronaut/">Micronaut</a>.
But thanks to a recent <a href="https://docs.langchain4j.dev/tutorials/mcp-stdio-server#start-the-stdio-server">community contribution</a>
to <strong><a href="https://docs.langchain4j.dev">LangChain4j</a></strong>,
and the simplicity of <strong><a href="https://jbang.dev">JBang</a></strong>,
building a local MCP server in Java is even easier and with zero boilerplate.</p>
<p>In this post, <strong>we&rsquo;ll build a standalone Java MCP server that runs over STDIO</strong>,
perfect for local integration with tools like the <a href="https://geminicli.com/">Gemini CLI</a>
or other locally running agentic tools supporting MCP servers.</p>
<hr />
<h2 id="the-stack-why-this-matters">The Stack: Why This Matters</h2>
<p>To keep things lightweight, we’re using two powerful tools:</p>
<ol>
<li><strong>LangChain4j</strong>:
The leading framework for building AI-powered Java applications.
It now includes a dedicated MCP server module for the STDIO protocol
(in addition to the existing <a href="https://docs.langchain4j.dev/tutorials/mcp">MCP client module</a>.)</li>
<li><strong>JBang</strong>:
A tool that lets you run Java files as scripts.
No <code>pom.xml</code>, no Gradle builds, just a single <code>.java</code> file with your dependencies declared right at the top.</li>
</ol>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition important">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>
        <span>Requirement</span>
      </div>
      <div class="admonition-content">
        <p>The key requirement here is that you&rsquo;ll need to <a href="https://www.jbang.dev/download/">install JBang</a> if you haven&rsquo;t already.</p>
      </div>
    </div><h2 id="the-code-a-standalone-mcp-server">The Code: A Standalone MCP Server</h2>
<p>Here is a complete, runnable MCP server in a single Java file. This server exposes a &ldquo;Calculator&rdquo; tool to any MCP-compatible LLM.</p>
<p>No build file, no project directory structure or anything.
<strong>Just a single Java file.</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">///usr/bin/env jbang &#34;$0&#34; &#34;$@&#34; ; exit $?</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//DEPS dev.langchain4j:langchain4j-core:1.11.0</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//DEPS dev.langchain4j:langchain4j-community-mcp-server:1.11.0-beta19</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//DEPS org.slf4j:slf4j-simple:2.0.17</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//JAVA 21</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.agent.tool.Tool</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.community.mcp.server.McpServer</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.community.mcp.server.transport.StdioMcpServerTransport</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">org.slf4j.Logger</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">org.slf4j.LoggerFactory</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.List</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.concurrent.CountDownLatch</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">McpToolServer</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// Important: Redirect logs to System.err</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>System.<span style="color:#4070a0">setProperty</span>(<span style="color:#4070a0">&#34;org.slf4j.simpleLogger.logFile&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                           </span><span style="color:#4070a0">&#34;System.err&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>Logger<span style="color:#bbb"> </span>log<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>LoggerFactory.<span style="color:#4070a0">getLogger</span>(McpToolServer.<span style="color:#4070a0">class</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">main</span>(String<span style="color:#666">[]</span><span style="color:#bbb"> </span>args)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">throws</span><span style="color:#bbb"> </span>Exception<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Starting LangChain4j MCP Server...&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// 1. Define your tools</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>CalculatorTools<span style="color:#bbb"> </span>tools<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>CalculatorTools();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// 2. Wrap them in an McpServer</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>McpServer<span style="color:#bbb"> </span>server<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>McpServer(List.<span style="color:#4070a0">of</span>(tools));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// 3. Connect to the STDIO transport</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>StdioMcpServerTransport<span style="color:#bbb"> </span>transport<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>StdioMcpServerTransport(server);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;MCP Server started successfully on STDIO.&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// Keep the script alive</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>CountDownLatch(1).<span style="color:#4070a0">await</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// Define MCP tools</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">CalculatorTools</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@Tool</span>(<span style="color:#4070a0">&#34;Calculates the sum of two numbers&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#902000">double</span><span style="color:#bbb"> </span><span style="color:#06287e">add</span>(<span style="color:#902000">double</span><span style="color:#bbb"> </span>a,<span style="color:#bbb"> </span><span style="color:#902000">double</span><span style="color:#bbb"> </span>b)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Called add({}, {})&#34;</span>,<span style="color:#bbb"> </span>a,<span style="color:#bbb"> </span>b);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>a<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>b;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@Tool</span>(<span style="color:#4070a0">&#34;Calculates the square root of a number&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#902000">double</span><span style="color:#bbb"> </span><span style="color:#06287e">sqrt</span>(<span style="color:#902000">double</span><span style="color:#bbb"> </span>x)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Called sqrt({})&#34;</span>,<span style="color:#bbb"> </span>x);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Math.<span style="color:#4070a0">sqrt</span>(x);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><h3 id="breaking-it-down">Breaking It Down</h3>
<ul>
<li><strong>JBang Directives</strong>:
The <code>//DEPS</code> lines at the top handle all your dependencies.
When you run this file, JBang downloads everything automatically
(like Groovy&rsquo;s <a href="https://docs.groovy-lang.org/latest/html/documentation/grape.html">@Grab</a> annotation).</li>
<li><strong>The <code>@Tool</code> Annotation</strong>:
Any public method annotated with LangChain4j&rsquo;s <code>@Tool</code> annotation is automatically converted into a JSON-RPC tool specification that the LLM can understand.</li>
<li><strong><code>StdioMcpServerTransport</code></strong>:
Most local MCP clients communicate via Standard Input/Output.
This transport layer handles the JSON-RPC handshake for you.</li>
</ul>
<hr />
<h2 id="the-secret-sauce-logging-to-systemerr">The &ldquo;Secret Sauce&rdquo;: Logging to <code>System.err</code></h2>
<p>There is one critical rule for MCP servers running over STDIO: <strong><code>System.out</code> is for communication ONLY.</strong></p>
<p>I could have reduced the size of the example above almost by half by removing all the logging code.
But it&rsquo;s important to highlight it, if you want to be able to somehow log information along the way.</p>
<p>The MCP protocol uses <code>stdout</code> to send JSON-RPC messages back and forth.
If your application (or a library) prints a generic <code>INFO: Hello World</code> to <code>stdout</code>,
it will corrupt the JSON stream and crash the connection.</p>
<p>That’s why we use this static block:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>System.<span style="color:#4070a0">setProperty</span>(<span style="color:#4070a0">&#34;org.slf4j.simpleLogger.logFile&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;System.err&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>By forcing all logs to <code>stderr</code>, we keep the communication channel clean while still being able to see our logs in the terminal.</p>
<hr />
<h2 id="testing-your-server">Testing Your Server</h2>
<p>Before you plug it into an LLM, you can test it using the <strong><a href="https://modelcontextprotocol.io/docs/tools/inspector">MCP Inspector</a></strong>.
It’s a handy web UI that lets you see exactly what’s happening under the hood.</p>
<p>Run your server with the following command:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>npx @modelcontextprotocol/inspector jbang run --quiet McpToolServer.java
</span></span></code></pre></div>
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Notes</span>
      </div>
      <div class="admonition-content">
        <ul>
<li>You&rsquo;ll need to have <code>npx</code> installed to run the MCP inspector.</li>
<li>The <code>--quiet</code> flag tells JBang to stop printing build messages to stdout!
We don&rsquo;t want JBang to interfere with the STDIO protocol either!</li>
</ul>
      </div>
    </div><p>Once the inspector is running, you can click to connect to the server, list the tools, select a tool,
all in your browser, and watch your Java code execute in real-time.</p>
<p><figure>
  <a href="#img-adbed030017340d50425d7c511027fe9">
    <img src="/img/mcp/mcp-jbang-lc4j.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-adbed030017340d50425d7c511027fe9">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/mcp/mcp-jbang-lc4j.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h2 id="running-it-in-gemini-cli">Running it in Gemini CLI</h2>
<p>To let Gemini use your new tool, add it to your <code>~/.gemini/settings.json</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;mcpServers&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;java-calc&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;command&#34;</span>: <span style="color:#4070a0">&#34;jbang&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;args&#34;</span>: [<span style="color:#4070a0">&#34;run&#34;</span>, <span style="color:#4070a0">&#34;--quiet&#34;</span>, <span style="color:#4070a0">&#34;/path/to/McpToolServer.java&#34;</span>]
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Now, when you ask Gemini &ldquo;What is the square root of 144?&rdquo;, it will reach out to your JBang script, execute the Java method, and give you the answer.</p>
<p><figure>
  <a href="#img-55d91b265d805edfd60139757202afa5">
    <img src="/img/mcp/mcp-jbang-lc4j-gemini-cli.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-55d91b265d805edfd60139757202afa5">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/mcp/mcp-jbang-lc4j-gemini-cli.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Well&hellip; maybe it won&rsquo;t call the tool because the LLM already knows the answer to such a simple question &#x1f603;
thanks to its training data and understanding of simple math, but for more specific and complex tools, it should be called!
In my case, it actually figured out it should call it.
And the response seems correct to me &#x1f603;</p>
<p><figure>
  <a href="#img-15004912b9415815f79966ca8f9f87d7">
    <img src="/img/mcp/jbang-mcp-result.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-15004912b9415815f79966ca8f9f87d7">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/mcp/jbang-mcp-result.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<hr />
<h2 id="wrapping-up">Wrapping Up</h2>
<p>Building MCP servers doesn&rsquo;t have to be complex.
With <strong>LangChain4j</strong> and <strong>JBang</strong>, you get the best of both worlds: the power of the Java ecosystem with the agility of a scripting language!</p>
<p>So next time you need to give an LLM access to a legacy Java library or a complex calculation, remember:
you’re only one <code>@Tool</code> annotation away.</p>
<p>Java developers can be as agile as all the script kiddies!
Happy MCP server coding!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Advanced RAG — Understanding Reciprocal Rank Fusion in Hybrid Search</title><link>https://glaforge.dev/posts/2026/02/10/advanced-rag-understanding-reciprocal-rank-fusion-in-hybrid-search/</link><pubDate>Tue, 10 Feb 2026 08:52:11 +0100</pubDate><guid>https://glaforge.dev/posts/2026/02/10/advanced-rag-understanding-reciprocal-rank-fusion-in-hybrid-search/</guid><description>&lt;p>Today, let&amp;rsquo;s come back to one of my favorite generative AI topics:
&lt;a href="https://glaforge.dev/tags/retrieval-augmented-generation/">Retrieval Augmented Generation&lt;/a>, or RAG for short.&lt;/p>
&lt;p>In RAG, the quality of your &lt;em>generation&lt;/em> (when an LLM crafts its answer based on search results)
is only as good as your &lt;em>retrieval&lt;/em> (the actually retrieved search results).&lt;/p>
&lt;p>While vector search (semantic) and keyword search (&lt;a href="https://en.wikipedia.org/wiki/Okapi_BM25">BM25&lt;/a>) each have their strengths,
combining them often yields the best results.
That&amp;rsquo;s what we often call &lt;strong>Hybrid Search&lt;/strong>: combining two search techniques or the results of different searches with slight variations.&lt;/p></description><content:encoded>
<![CDATA[<p>Today, let&rsquo;s come back to one of my favorite generative AI topics:
<a href="https://glaforge.dev/tags/retrieval-augmented-generation/">Retrieval Augmented Generation</a>, or RAG for short.</p>
<p>In RAG, the quality of your <em>generation</em> (when an LLM crafts its answer based on search results)
is only as good as your <em>retrieval</em> (the actually retrieved search results).</p>
<p>While vector search (semantic) and keyword search (<a href="https://en.wikipedia.org/wiki/Okapi_BM25">BM25</a>) each have their strengths,
combining them often yields the best results.
That&rsquo;s what we often call <strong>Hybrid Search</strong>: combining two search techniques or the results of different searches with slight variations.</p>
<p>But how do you meaningfully combine a cosine similarity score of <code>0.85</code> (from vector search) with a BM25 score of <code>12.4</code>?
Those values are on two distinct unrelated scales!</p>
<p>Enter <strong>Reciprocal Rank Fusion (RRF)</strong>.</p>
<p>I vibe-coded a little <a href="https://storage.googleapis.com/public-bucket-for-demos/index.html">RRF simulator</a>
that shows how two lists of documents are ranked into one.
For the impatient, feel free to go ahead and play with it,
otherwise, you&rsquo;ll find more information at the bottom of this article
on how to use this simulator.</p>
<p><a href="https://storage.googleapis.com/public-bucket-for-demos/index.html"><figure>
  <a href="#img-ce3bbeff647065d370e0f565725e5951">
    <img src="/img/rag/rrf-simulator.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-ce3bbeff647065d370e0f565725e5951">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/rag/rrf-simulator.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</a></p>
<h2 id="what-is-rrf">What is RRF?</h2>
<p>RRF is a robust, <em>&ldquo;zero-shot&rdquo;</em> algorithm for merging search results from different retrieval methods.
The technique was formally introduced by Gordon V. Cormack and his colleagues in their 2009 SIGIR paper,
<a href="https://cormack.uwaterloo.ca/cormacksigir09-rrf.pdf"><strong>&ldquo;Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods&rdquo;</strong></a>.</p>
<p>Instead of trying to somehow normalize arbitrary scores, RRF ignores the scores entirely and focuses on <strong>rank</strong>.</p>
<p>It operates on a simple premise: <strong>Documents that appear at the top of multiple lists are likely the most relevant.</strong>
In their research, the authors found that RRF consistently outperformed individual search systems and more complex fusion methods,
providing a stable and scalable way to combine diverse ranking signals.</p>
<p><figure>
  <a href="#img-9d58008cfc354468af6413247f29b6cf">
    <img src="/img/sketchnotes/rrf-1.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-9d58008cfc354468af6413247f29b6cf">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/sketchnotes/rrf-1.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h2 id="the-formula-simplified">The Formula Simplified</h2>
<p>The RRF score for a document is calculated as:</p>

  <div class="math-container">
    <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>Score</mtext><mo stretchy="false">(</mo><mi>d</mi><mo stretchy="false">)</mo><mo>=</mo><munder><mo>∑</mo><mrow><mi>r</mi><mo>∈</mo><mi>R</mi></mrow></munder><mfrac><mn>1</mn><mrow><mi>k</mi><mo>+</mo><mtext>rank</mtext><mo stretchy="false">(</mo><mi>r</mi><mo separator="true">,</mo><mi>d</mi><mo stretchy="false">)</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">
\text{Score}(d) = \sum_{r \in R} \frac{1}{k + \text{rank}(r, d)}
</annotation></semantics></math></span>
  </div>

<ul>
<li><strong>
  <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>rank</mtext><mo stretchy="false">(</mo><mi>r</mi><mo separator="true">,</mo><mi>d</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\text{rank}(r, d)</annotation></semantics></math></span>

</strong>: The position of the document in a specific search result list (1st, 2nd, etc.).</li>
<li><strong>
  <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span>

</strong>: A smoothing constant, typically set to <strong>60</strong>.</li>
</ul>
<h3 id="why-is-k-so-important">Why is 
  <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span>

 so important?</h3>
<p><figure>
  <a href="#img-3047a52f34cde515564835c3abc01e25">
    <img src="/img/sketchnotes/rrf-2.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-3047a52f34cde515564835c3abc01e25">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/sketchnotes/rrf-2.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Think of 
  <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span>

 as a &ldquo;balance&rdquo; dial.</p>
<ul>
<li><strong>If 
  <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span>

 is low (e.g., 1):</strong> The formula gives a massive advantage to the top-ranked items.
This configuration favors <strong>Precision</strong> — trusting that the absolute top results are correct and allowing a single high-performing retriever to dominate.</li>
<li><strong>If 
  <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span>

 is high (e.g., 60):</strong> The advantage of being #1 shrinks. This configuration improves <strong>Recall</strong> and <strong>Consensus</strong>.
It ensures that even if the &ldquo;perfect&rdquo; result is buried at rank #10 across multiple lists, it will still rise to the top.</li>
</ul>
<p><strong>Why use 60?</strong>
By setting 
  <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi><mo>=</mo><mn>60</mn></mrow><annotation encoding="application/x-tex">k=60</annotation></semantics></math></span>

 (the industry standard), RRF prioritizes <strong>consensus</strong> over individual outliers.
It ensures that a document appearing consistently (e.g., ranked #10 in <em>both</em> keyword and vector search) will score higher than a document that is #1 in only one list but completely missing from the others.</p>
<p>It rewards documents that <strong>multiple</strong> algorithms agree on, rather than letting a single outlier dominate the results.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>A Note on Document Sets</span>
      </div>
      <div class="admonition-content">
        <p>RRF is fundamentally designed to find <strong>consensus</strong>.
This means it works best when your different retrieval methods are looking at the same overall set of documents and return some overlaps.</p>
<p>If your search results are <strong>totally disjoint</strong> (meaning no document appears in more than one list),
RRF will simply interleave the results: you&rsquo;ll get the #1 from list A, then #1 from list B, followed by the #2 from list A, and so on.
The algorithm only truly begins to &ldquo;fuse&rdquo; and re-sort the results when documents start appearing in multiple lists.</p>
      </div>
    </div><h2 id="why-use-rrf-in-rag">Why Use RRF in RAG?</h2>
<ol>
<li><strong>Normalization Free:</strong> You don&rsquo;t need to know the distribution of your vector or BM25 scores. RRF works purely on position.</li>
<li><strong>Scalability:</strong> It&rsquo;s extremely efficient for sharded, billion-scale indices where global score normalization is expensive.</li>
<li><strong>Candidate Selection:</strong> RRF is an excellent &ldquo;first stage&rdquo; reranker. A common pattern is to retrieve the top 100 documents via RRF,
and then use a more expensive (but precise) <a href="https://medium.com/@sujathamudadla1213/what-is-cross-encoder-fec22b58f16c">Cross-Encoder</a>
(a <em>reranker</em> model) to rank the top 10 for the LLM context window (see below more for details).</li>
</ol>
<h2 id="the-two-stage-architecture-rrf--cross-encoder">The Two-Stage Architecture: RRF + Cross-Encoder</h2>
<p><figure>
  <a href="#img-6fbea3539afb6aadf3541909e79059a7">
    <img src="/img/sketchnotes/rrf-3.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-6fbea3539afb6aadf3541909e79059a7">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/sketchnotes/rrf-3.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>While RRF is excellent at merging lists, it lacks deep semantic understanding of the query-document relationship.
This is where <strong>Cross-Encoders</strong> shine — models like <a href="https://en.wikipedia.org/wiki/BERT_(language_model)">BERT</a>
that score the actual interaction between query and text.
However, they are computationally expensive and slow.</p>
<p>The industry standard pattern is a &ldquo;Two-Stage&rdquo; architecture:</p>
<ol>
<li><strong>Stage 1 (Candidate Selection):</strong> Use Hybrid Search (Vector + Keyword) fused with <strong>RRF</strong> to retrieve a broad pool of candidates (e.g., top 100).
This ensures high <strong>Recall</strong> — the right answer is likely <em>somewhere</em> in this list.</li>
<li><strong>Stage 2 (Precision Reranking):</strong> Pass only these top 100 candidates to a <strong>Cross-Encoder</strong>.
The model re-scores them based on deep relevance, picking the absolute best 5-10 chunks for the LLM&rsquo;s context window.</li>
</ol>
<p>This pipeline gives you the best of both worlds: the speed and breadth of RRF with the precision of a Cross-Encoder.</p>
<h2 id="going-further-rag-fusion">Going Further: RAG-Fusion</h2>
<p><figure>
  <a href="#img-c296f20bb5e40558b8f8adf2e92d8f83">
    <img src="/img/sketchnotes/rrf-4.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-c296f20bb5e40558b8f8adf2e92d8f83">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/sketchnotes/rrf-4.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>RAG-Fusion takes the hybrid approach a step further.
This technique was introduced by Zackary Rackauckas in the 2024 paper,
<a href="https://arxiv.org/abs/2402.03367"><strong>&ldquo;RAG-Fusion: a New Take on Retrieval-Augmented Generation&rdquo;</strong></a>.
It uses an LLM to generate multiple variations of the user&rsquo;s original query to &ldquo;broaden the net&rdquo; and find relevant context that a single query might miss.</p>
<p>The process follows a clever loop:</p>
<ol>
<li><strong>Multi-Query Generation:</strong> An LLM generates 3-5 different versions of the user&rsquo;s query (e.g., synonyms, rephrasings, or breaking down a complex question).</li>
<li><strong>Parallel Retrieval:</strong> Each variation is sent to the search engine (both Vector and Keyword).</li>
<li><strong>RRF Aggregation:</strong> All resulting lists are fused using RRF.</li>
</ol>
<p>By using RRF to merge results from <em>multiple</em> query variations, the system naturally filters out &ldquo;topic drift.&rdquo;
Documents that appear consistently across many query variants rise to the top, while noise from a single poor query variation is pushed down.
This &ldquo;consensus&rdquo; approach significantly reduces hallucination rates by ensuring the LLM is provided with content validated by multiple search angles.</p>
<h2 id="rrf-in-the-wild-langchain4j">RRF in the Wild: LangChain4j</h2>
<p>RRF isn&rsquo;t just a theoretical concept; it&rsquo;s a standard component in modern AI stacks. <a href="https://docs.langchain4j.dev"><strong>LangChain4j</strong></a>,
the popular Java library for building LLM-powered applications, uses RRF as its default mechanism for aggregating results from multiple sources.</p>
<p>The <code>DefaultContentAggregator</code> class in LangChain4j employs a <code>ReciprocalRankFuser</code> to merge ranked lists of content.
This means if you configure a RAG pipeline with multiple retrievers (e.g., one for recent web data and one for internal documents),
LangChain4j automatically applies RRF to give you the best of both worlds without any manual tuning.</p>
<p>Here is how you can set up a hybrid retrieval system in LangChain4j that implicitly uses RRF:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// 1. Define your retrievers</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ContentRetriever<span style="color:#bbb"> </span>bm25Retriever<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>...;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ContentRetriever<span style="color:#bbb"> </span>vectorSearchRetriever<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>EmbeddingStoreContentRetriever.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">embeddingStore</span>(embeddingStore)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">embeddingModel</span>(embeddingModel)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">maxResults</span>(10)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// 2. Combine them in the RetrievalAugmentor</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// DefaultRetrievalAugmentor uses</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// DefaultContentAggregator which uses RRF</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>RetrievalAugmentor<span style="color:#bbb"> </span>retrievalAugmentor<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>DefaultRetrievalAugmentor.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">contentRetriever</span>(bm25Retriever)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">contentRetriever</span>(vectorSearchRetriever)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// 3. Configure the augmentor on the AI service</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Assistant<span style="color:#bbb"> </span>assistant<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>AiServices.<span style="color:#4070a0">builder</span>(Assistant.<span style="color:#4070a0">class</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>...<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">retrievalAugmentor</span>(retrievalAugmentor)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>By simply adding multiple retrievers, the <code>DefaultContentAggregator</code> kicks in, calculating the 
  <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mn>1</mn><mrow><mi>k</mi><mo>+</mo><mtext>rank</mtext></mrow></mfrac></mrow><annotation encoding="application/x-tex"> \frac{1}{k + \text{rank}} </annotation></semantics></math></span>

 score for every item found by <em>either</em> retriever and re-sorting them into a single, high-quality context for your LLM.</p>
<h2 id="try-the-simulator">Try the Simulator</h2>
<p>To truly understand how the smoothing constant 
  <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span>

 impacts rankings and how different lists merge, I&rsquo;ve built a <strong>Reciprocal Rank Fusion Simulator</strong>.</p>
<p>You can experiment with different document rankings and see the fusion math in real-time here:
👉 <strong><a href="https://storage.googleapis.com/public-bucket-for-demos/index.html">Launch RRF Simulator</a></strong></p>
<p>Use this tool to visualize how RRF balances precision (favoring top ranks) vs. consensus (favoring agreement across lists) and tune your intuition for hybrid search architectures.</p>
<ul>
<li><strong>Interactive Rank Experimentation</strong>:
Create new lists, add/remove documents, shuffle the lists, etc.
Use drag-and-drop to reorder results in two independent search engines.
Since RRF ignores raw scores and focuses only on position,
you can see exactly how moving a document up or down one list impacts its final &ldquo;fused&rdquo; standing.</li>
<li><strong>Visualizing Consensus</strong>:
The simulation demonstrates RRF’s &ldquo;consensus&rdquo; logic, indeed
documents that appear in both lists (even at mediocre ranks)
often outperform documents that rank #1 in only one list.
This highlights why hybrid search is so effective.</li>
<li><strong>Real-Time Parameter Tuning</strong>:
By adjusting the 
  <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span>

 constant, you can see how the algorithm&rsquo;s sensitivity changes.
You&rsquo;ll observe how a lower prioritizes &ldquo;top-heavy&rdquo; results,
while a higher gives more weight to documents found deeper in the search results.</li>
</ul>
<h2 id="summary">Summary</h2>
<p>I hope this article helped you get a better <strong>intuition</strong> of <strong>Reciprocal Rank Fusion</strong>, why it&rsquo;s so useful, and how it works!
By <strong>focusing on rank</strong> rather than arbitrary scores, RRF provides a robust and scalable way to merge diverse search results,
making it a cornerstone of <strong>modern hybrid search</strong> and <strong>advanced RAG architectures</strong>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Decoded: How Google AI Studio Securely Proxies Gemini API Requests</title><link>https://glaforge.dev/posts/2026/02/09/decoded-how-google-ai-studio-securely-proxies-gemini-api-requests/</link><pubDate>Mon, 09 Feb 2026 08:52:18 +0100</pubDate><guid>https://glaforge.dev/posts/2026/02/09/decoded-how-google-ai-studio-securely-proxies-gemini-api-requests/</guid><description>&lt;p>If you&amp;rsquo;ve recently vibe-coded and exported a Gemini-powered app from &lt;a href="https://aistudio.google.com/">Google AI Studio&lt;/a>
to host it online on Google &lt;a href="https://cloud.google.com/run">Cloud Run&lt;/a>,
you might have noticed a &lt;code>server/&lt;/code> directory containing a Node.js application.
This isn&amp;rsquo;t just a simple file server; it&amp;rsquo;s a clever &lt;em>&amp;ldquo;transparent proxy&amp;rdquo;&lt;/em> designed to solve a classic problem in frontend AI development:&lt;/p>
&lt;p>&lt;strong>How do I use my API key without leaking it to the browser?&lt;/strong>&lt;/p>
&lt;p>In this post (although vibe-coding is supposed to be all about &lt;em>not&lt;/em> looking at the code at all)
we&amp;rsquo;ll dissect exactly how this architecture works, why it&amp;rsquo;s safer than a client-side key, and where its security limits lie.&lt;/p></description><content:encoded>
<![CDATA[<p>If you&rsquo;ve recently vibe-coded and exported a Gemini-powered app from <a href="https://aistudio.google.com/">Google AI Studio</a>
to host it online on Google <a href="https://cloud.google.com/run">Cloud Run</a>,
you might have noticed a <code>server/</code> directory containing a Node.js application.
This isn&rsquo;t just a simple file server; it&rsquo;s a clever <em>&ldquo;transparent proxy&rdquo;</em> designed to solve a classic problem in frontend AI development:</p>
<p><strong>How do I use my API key without leaking it to the browser?</strong></p>
<p>In this post (although vibe-coding is supposed to be all about <em>not</em> looking at the code at all)
we&rsquo;ll dissect exactly how this architecture works, why it&rsquo;s safer than a client-side key, and where its security limits lie.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>When exporting/downloading an AI Studio generated app, you won&rsquo;t see what I&rsquo;m going to explain below.
It&rsquo;s when you export the app to Cloud Run that the mechanism explained here is put in place.
I looked at the code that was deployed on Cloud Run, from the Google Cloud Console.</p>
      </div>
    </div><h2 id="the-problem-client-side-keys">The Problem: Client-Side Keys</h2>
<p>When building an app with a React, Vue, or vanilla JS frontend that talks to an AI model, the path of least resistance would often be:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// ❌ DANGEROUS: Do not do this in production
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">const</span> API_KEY <span style="color:#666">=</span> <span style="color:#4070a0">&#34;AIzaSy...&#34;</span>;
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> genAI <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> GoogleGenerativeAI(API_KEY);
</span></span></code></pre></div><p>The moment you deploy this, your API key is visible in the browser&rsquo;s &ldquo;Network&rdquo; tab of the Dev tools, or by inspecting the JavaScript source.
A malicious actor can grab your key and use your quota for their own projects, potentially racking up bills or exhausting your limits.</p>
<p><figure>
  <a href="#img-0205ea0975b4053f37e692e625814967">
    <img src="/img/nano-banana/gloops-tokens-consumed.jpg"
      alt="Scary illustration of a person on his computer horrified to discover on his screen that his LLM tokens have been consumed because of a leaked API key"
       />
  </a>
  <figcaption>Scary illustration of a person on his computer horrified to discover on his screen that his LLM tokens have been consumed because of a leaked API key</figcaption>
</figure>
<div class="lightbox" id="img-0205ea0975b4053f37e692e625814967">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/nano-banana/gloops-tokens-consumed.jpg"
    alt="Scary illustration of a person on his computer horrified to discover on his screen that his LLM tokens have been consumed because of a leaked API key"
     />
  <div class="lightbox-caption">Scary illustration of a person on his computer horrified to discover on his screen that his LLM tokens have been consumed because of a leaked API key</div>
</div>
</p>
<h2 id="the-solution-the-transparent-proxy">The Solution: The &ldquo;Transparent&rdquo; Proxy</h2>
<p>&#x1f4a1; <a href="https://aistudio.google.com/">Google AI Studio</a>&rsquo;s exported starter code uses a <strong>Node.js proxy server</strong>
combined with <strong>Service Workers</strong> to hide the key while letting you write frontend code as if you were calling the API directly.</p>
<p>The classical approach is to have your frontend call your backend code, and it&rsquo;s only your backend code that has access to the API key.
Here, the approach taken by AI Studio for Cloud Run deployment is to let developers continue to write frontend code <em>as usual</em>,
calling the generative AI API as if it were a direct call from the frontend&hellip; But in reality,
the call is intercepted and proxied to a server backend that takes care of making the real call to the AI model,
and handle the API key on the backend, without ever exposing it.</p>
<h3 id="part-1-the-server">Part 1: The Server</h3>
<p>The heart of the system is an Express.js server (<code>server/server.js</code>). It serves your frontend files but also listens on a special endpoint: <code>/api-proxy</code>.</p>
<p>When a request hits this endpoint, the server:</p>
<ol>
<li><strong>Injects the API Key:</strong> It takes the key from a secure environment variable on the server-side (<code>GEMINI_API_KEY</code>).</li>
<li><strong>Forwards the Request:</strong> It sends the modified request to Google&rsquo;s real API (<code>generativelanguage.googleapis.com</code>).</li>
<li><strong>Streams the Response:</strong> It pipes the answer back to your browser.</li>
</ol>
<p>Here is the critical logic in <code>server.js</code> where the key is added:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// server/server.js
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// ... inside the /api-proxy route handler ...
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Prepare headers for the outgoing request
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">const</span> outgoingHeaders <span style="color:#666">=</span> {};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Copy most headers from the incoming request (content-type, etc.)
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// ... (code to copy headers) ...
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// 🔐 KEY INJECTION HAPPENS HERE
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>outgoingHeaders[<span style="color:#4070a0">&#39;X-Goog-Api-Key&#39;</span>] <span style="color:#666">=</span> process.env.GEMINI_API_KEY;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> axiosConfig <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>    method<span style="color:#666">:</span> req.method,
</span></span><span style="display:flex;"><span>    url<span style="color:#666">:</span> <span style="color:#4070a0">`https://generativelanguage.googleapis.com/</span><span style="color:#70a0d0">${</span>targetPath<span style="color:#70a0d0">}</span><span style="color:#4070a0">`</span>,
</span></span><span style="display:flex;"><span>    headers<span style="color:#666">:</span> outgoingHeaders,
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// ...
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Forward the request to Google
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">const</span> apiResponse <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">await</span> axios(axiosConfig);
</span></span></code></pre></div><p>The <strong>frontend never receives the key</strong>. It only receives the <em>results</em> of the API call.</p>
<h3 id="part-2-client-side-interception">Part 2: Client-Side Interception</h3>
<p>If you look at the frontend code (e.g., in <code>App.tsx</code>), you might see standard calls to the Gemini API
(usually it&rsquo;s implemented in a dedicated <code>services/geminiService.ts</code> file):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Frontend code looks like it&#39;s calling Google directly!
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">const</span> model <span style="color:#666">=</span> genAI.getGenerativeModel({
</span></span><span style="display:flex;"><span>  model<span style="color:#666">:</span> <span style="color:#4070a0">&#34;gemini-3-flash-preview&#34;</span>,
</span></span><span style="display:flex;"><span>  temperaturecontents<span style="color:#666">:</span> {...}
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> result <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">await</span> model.generateContent(prompt);
</span></span></code></pre></div><p>How does this work if the frontend doesn&rsquo;t have the key?</p>
<p>The server injects two scripts into your <code>index.html</code> at runtime:</p>
<ol>
<li><code>websocket-interceptor.js</code></li>
<li><code>service-worker.js</code></li>
</ol>
<h4 id="the-service-worker">The Service Worker</h4>
<p>The <code>service-worker.js</code> acts like a network traffic cop inside your browser. It monitors all outgoing fetch requests.
If it sees a request headed for <code>generativelanguage.googleapis.com</code>, it <strong>stops it</strong> and redirects it to your local server instead.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// server/public/service-worker.js
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> TARGET_URL_PREFIX <span style="color:#666">=</span> <span style="color:#4070a0">&#39;https://generativelanguage.googleapis.com&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>self.addEventListener(<span style="color:#4070a0">&#39;fetch&#39;</span>, (event) =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">const</span> requestUrl <span style="color:#666">=</span> event.request.url;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">if</span> (requestUrl.startsWith(TARGET_URL_PREFIX)) {
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// ✋ Stop! Don&#39;t go to Google directly.
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#60a0b0;font-style:italic">// 👉 Go to our local proxy instead.
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#007020;font-weight:bold">const</span> remainingPath <span style="color:#666">=</span> requestUrl.substring(TARGET_URL_PREFIX.length);
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">const</span> proxyUrl <span style="color:#666">=</span> <span style="color:#4070a0">`</span><span style="color:#70a0d0">${</span>self.location.origin<span style="color:#70a0d0">}</span><span style="color:#4070a0">/api-proxy</span><span style="color:#70a0d0">${</span>remainingPath<span style="color:#70a0d0">}</span><span style="color:#4070a0">`</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// Forward the request to /api-proxy
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    event.respondWith(fetch(<span style="color:#007020;font-weight:bold">new</span> Request(proxyUrl, { ... })));
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>This &ldquo;transparent&rdquo; redirection means you don&rsquo;t have to change your frontend code to point to <code>http://localhost:3000/api-proxy</code>.
You just write standard SDK code, and the Service Worker handles the routing.</p>
<h4 id="the-websocket-interceptor">The WebSocket Interceptor</h4>
<p>For streaming features or chat, the Gemini API uses WebSockets. Service Workers cannot easily intercept WebSocket connections,
so the solution uses a different trick: <strong>Monkey Patching</strong>.</p>
<p>The <code>websocket-interceptor.js</code> overwrites the global browser <code>WebSocket</code> constructor.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// server/public/websocket-interceptor.js
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> originalWebSocket <span style="color:#666">=</span> <span style="color:#007020">window</span>.WebSocket;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020">window</span>.WebSocket <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> <span style="color:#007020">Proxy</span>(originalWebSocket, {
</span></span><span style="display:flex;"><span>  construct(target, args) {
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">let</span> [url, protocols] <span style="color:#666">=</span> args;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// Check if the connection is destined for Gemini
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#007020;font-weight:bold">if</span> (url.includes(<span style="color:#4070a0">&#39;generativelanguage.googleapis.com&#39;</span>)) {
</span></span><span style="display:flex;"><span>       <span style="color:#60a0b0;font-style:italic">// Redirect to our local proxy endpoint
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>       url <span style="color:#666">=</span> url.replace(<span style="color:#4070a0">&#39;wss://generativelanguage.googleapis.com&#39;</span>,
</span></span><span style="display:flex;"><span>                         <span style="color:#4070a0">`wss://</span><span style="color:#70a0d0">${</span><span style="color:#007020">window</span>.location.host<span style="color:#70a0d0">}</span><span style="color:#4070a0">/api-proxy`</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// Create the WebSocket with the new URL
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#007020;font-weight:bold">return</span> <span style="color:#007020;font-weight:bold">new</span> originalWebSocket(url, protocols);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><h2 id="security-reality-check">Security Reality Check</h2>
<p>Is this secure? <strong>Yes and No.</strong></p>
<h3 id="-the-good-credential-protection">✅ The Good: Credential Protection</h3>
<p>This architecture successfully hides the &ldquo;Secret String&rdquo; (your API Key), as if you had written your own backened server.</p>
<ul>
<li>It is <strong>not</strong> in the JavaScript bundle.</li>
<li>It is <strong>not</strong> in the network traffic (except between your server and Google).</li>
<li>A user cannot &ldquo;copy-paste&rdquo; your key to use in their own unrelated backend script.</li>
</ul>
<h3 id="-the-bad-the-open-proxy-risk">⚠️ The Bad: The &ldquo;Open Proxy&rdquo; Risk</h3>
<p>Because the server is a &ldquo;dumb pipe&rdquo; — it blindly signs <em>any</em> request sent to <code>/api-proxy</code>.
A malicious user on your site can still abuse your quota, by opening Chrome DevTools and running:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// This will be intercepted by the Service Worker and proxied!
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>fetch(<span style="color:#4070a0">&#39;https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent&#39;</span>, {
</span></span><span style="display:flex;"><span>    method<span style="color:#666">:</span> <span style="color:#4070a0">&#39;POST&#39;</span>,
</span></span><span style="display:flex;"><span>    body<span style="color:#666">:</span> JSON.stringify({ contents<span style="color:#666">:</span> [{ parts<span style="color:#666">:</span> [{ text<span style="color:#666">:</span> <span style="color:#4070a0">&#34;Generate 5000 words of spam...&#34;</span> }] }] })
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div><p>Your server will happily stamp this request with your API key and send it to Google.</p>
<h3 id="-the-mitigation-rate-limiting">🛡️ The Mitigation: Rate Limiting</h3>
<p>The AI Studio team anticipated this. The generated server includes <strong>Rate Limiting</strong> to prevent a single user from draining your quota instantly.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// server/server.js
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> proxyLimiter <span style="color:#666">=</span> rateLimit({
</span></span><span style="display:flex;"><span>    windowMs<span style="color:#666">:</span> <span style="color:#40a070">15</span> <span style="color:#666">*</span> <span style="color:#40a070">60</span> <span style="color:#666">*</span> <span style="color:#40a070">1000</span>, <span style="color:#60a0b0;font-style:italic">// 15 minutes
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    max<span style="color:#666">:</span> <span style="color:#40a070">100</span>, <span style="color:#60a0b0;font-style:italic">// Limit each IP to 100 requests per window
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    message<span style="color:#666">:</span> <span style="color:#4070a0">&#39;Too many requests from this IP...&#39;</span>
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>app.use(<span style="color:#4070a0">&#39;/api-proxy&#39;</span>, proxyLimiter);
</span></span></code></pre></div><p>This ensures that while a user <em>can</em> make requests via your proxy, they are capped at a lower speed
(e.g., 100 requests per 15 minutes, but that might still be a bit too much!)</p>
<h2 id="conclusion">Conclusion</h2>
<p>I&rsquo;ve always been curious to understand how Google AI Studio was protecting the Gemini API key,
although the code appeared to directly make use of the API key on the frontend.
But the (real) code that is actually deployed on Cloud Run is pretty smart,
making use of some interesting tricks to not expose the key, and to mitigate the abuse of your quota.</p>
<p>The Google AI Studio proxy server is a nice piece of engineering for <strong>prototyping and demos</strong>.
It allows for a &ldquo;serverless-feeling&rdquo; frontend development experience while adhering to the basic security rule of keeping API keys on the server.</p>
<p>However, for a <strong>production application</strong>, you should eventually replace this generic proxy with specific backend endpoints
(e.g., <code>/api/generate-recipe</code>, <code>/api/chat-response</code>) that:</p>
<ol>
<li>Validate user input (<em>&ldquo;Is this actually a recipe request?&rdquo;</em>).</li>
<li>Authenticate the user (<em>&ldquo;Is this user logged in?&rdquo;</em>).</li>
<li>Apply strict business logic before calling the Gemini API.</li>
</ol>
<p>Even if Google AI Studio protects your API key to some extent, your quota can still be exhausted by a malicious user.
So if you&rsquo;re exposing such an application to the public, ask AI Studio to either add authentication,
or to request the user to pass their own API key.
Or even both!</p>
<p>I hope you found this exploration interesting!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Latest Gemini and Nano Banana Enhancements in LangChain4j</title><link>https://glaforge.dev/posts/2026/02/06/latest-gemini-and-nano-banana-enhancements-in-langchain4j/</link><pubDate>Fri, 06 Feb 2026 17:58:34 +0100</pubDate><guid>https://glaforge.dev/posts/2026/02/06/latest-gemini-and-nano-banana-enhancements-in-langchain4j/</guid><description>&lt;p>A few days ago, &lt;a href="https://github.com/langchain4j/langchain4j/releases/tag/1.11.0">LangChain4j 1.11.0&lt;/a> was released,
and with this version, a few notable enhancements to the support of the Gemini model family have landed.
Let&amp;rsquo;s dive in!&lt;/p>
&lt;h2 id="new-image-generation-models-gemini-25--30-preview-aka-banana-nano-banana">New Image Generation Models (Gemini 2.5 &amp;amp; 3.0 Preview, aka &amp;#x1f34c; Nano Banana)&lt;/h2>
&lt;link rel="stylesheet" href="https://glaforge.dev/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&amp;#43;AD&amp;#43;9EeYXUTm&amp;#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
&lt;div class="admonition note">
&lt;div class="admonition-header">&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">&lt;path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/>&lt;/svg>
&lt;span>Note&lt;/span>
&lt;/div>
&lt;div class="admonition-content">
&lt;p>Before showing some snippets of code, let me give you the link to the full documentation on the new image model:
&lt;a href="https://docs.langchain4j.dev/integrations/image-models/gemini">docs.langchain4j.dev/integrations/image-models/gemini&lt;/a>&lt;/p></description><content:encoded>
<![CDATA[<p>A few days ago, <a href="https://github.com/langchain4j/langchain4j/releases/tag/1.11.0">LangChain4j 1.11.0</a> was released,
and with this version, a few notable enhancements to the support of the Gemini model family have landed.
Let&rsquo;s dive in!</p>
<h2 id="new-image-generation-models-gemini-25--30-preview-aka-banana-nano-banana">New Image Generation Models (Gemini 2.5 &amp; 3.0 Preview, aka &#x1f34c; Nano Banana)</h2>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>Before showing some snippets of code, let me give you the link to the full documentation on the new image model:
<a href="https://docs.langchain4j.dev/integrations/image-models/gemini">docs.langchain4j.dev/integrations/image-models/gemini</a></p>
      </div>
    </div><p>There&rsquo;s a new <code>GoogleAiGeminiImageModel</code> class which allows <em>text-to-image</em> generation and <em>image editing</em> using the latest &#x1f34c; <strong>Nano Banana</strong> models.</p>
<p>Supported Models:</p>
<ul>
<li><code>gemini-2.5-flash-image</code> <em>(Nano Banana)</em>: Optimized for speed.</li>
<li><code>gemini-3-pro-image-preview</code> <em>(Nano Banana Pro)</em>: High-fidelity, up to 4K resolution.</li>
</ul>
<p>Features:</p>
<ul>
<li><strong>Text-to-Image</strong>: Generate images from prompts.</li>
<li><strong>Image Editing</strong>: Edit existing images using text prompts (with optional mask support).</li>
<li><strong>Search Grounding</strong>: Ground image generation in Google Search results.</li>
</ul>
<h3 id="text-to-image-generation">Text-to-Image Generation</h3>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>imageModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiImageModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-2.5-flash-image&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">aspectRatio</span>(<span style="color:#4070a0">&#34;16:9&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">imageSize</span>(<span style="color:#4070a0">&#34;2K&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>Image<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>imageModel.<span style="color:#4070a0">generate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;A cinematic shot of a futuristic city at sunset&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Save the generated image to a file</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Image<span style="color:#bbb"> </span>image<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>response.<span style="color:#4070a0">content</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#902000">byte</span><span style="color:#666">[]</span><span style="color:#bbb"> </span>imageBytes<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Base64.<span style="color:#4070a0">getDecoder</span>().<span style="color:#4070a0">decode</span>(image.<span style="color:#4070a0">base64Data</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Files.<span style="color:#4070a0">write</span>(Paths.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;output.png&#34;</span>),<span style="color:#bbb"> </span>imageBytes);<span style="color:#bbb">
</span></span></span></code></pre></div><p>As you can see, different configuration parameters are possible;</p>
<ul>
<li><code>aspectRatio</code>: among <code>16:9</code>, <code>4:3</code>, <code>3:2</code>, <code>1:1</code>,  <code>2:3</code>, <code>3:4</code>, and <code>9:16</code></li>
<li><code>imageSize</code>: among <code>1K</code>, <code>2K</code>, <code>4K</code></li>
</ul>
<h3 id="image-generation-with-google-search-grounding">Image Generation with Google Search Grounding</h3>
<p>A powerful capability of Nano Banana Pro is the ability to ground its image generation in Google Search results,
with the <code>useGoogleSearchGrounding(true)</code> flag.</p>
<p>It&rsquo;s a model that&rsquo;s able to search for image references on the web, or for the latest information about a topic.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>groundedModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiImageModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-3-pro-image-preview&#34;</span>)<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// search only in Pro</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">useGoogleSearchGrounding</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">aspectRatio</span>(<span style="color:#4070a0">&#34;1:1&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>Image<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>groundedResponse<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>groundedModel.<span style="color:#4070a0">generate</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    A kawaii illustration of the current weather forecast for Paris
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    showing the current temperature (in Celsius)
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Here, we want to create a <em>kawaii</em> illustration of the <em>current</em> weather in Paris.
So Nano Banana Pro is going to <strong>search on Google</strong> to find about the weather forecast at this point in time!</p>
<p>At the time of this writing, the forecast is:</p>
<p><figure>
  <a href="#img-8eab344f7b8a2411e8f2c1ef23bf583f">
    <img src="/img/nano-banana/paris_weather_illustration.jpg"
      alt="A Kawaii illustration of a little cloud and sun characters indicating a temperature of 13°C in Paris"
       />
  </a>
  <figcaption>A Kawaii illustration of a little cloud and sun characters indicating a temperature of 13°C in Paris</figcaption>
</figure>
<div class="lightbox" id="img-8eab344f7b8a2411e8f2c1ef23bf583f">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/nano-banana/paris_weather_illustration.jpg"
    alt="A Kawaii illustration of a little cloud and sun characters indicating a temperature of 13°C in Paris"
     />
  <div class="lightbox-caption">A Kawaii illustration of a little cloud and sun characters indicating a temperature of 13°C in Paris</div>
</div>
</p>
<p>Pretty <em>kawaii</em>, right? &#x1f603;</p>
<h2 id="google-maps-grounding">Google Maps Grounding</h2>
<p>You can now enable Google Maps grounding to allow the model to access real-world location data, including place IDs, addresses, and reviews.</p>

    <div class="admonition warning">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>
        <span>Warning</span>
      </div>
      <div class="admonition-content">
        <p>This is currently available on the 2.5 models, not (yet?) the 3.0 models.</p>
      </div>
    </div><div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>chatModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-2.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">allowGoogleMaps</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// Enable Google Maps tool</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">retrieveGoogleMapsWidgetToken</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// Optional: UI widget</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>chatModel.<span style="color:#4070a0">chat</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;Find the best restaurant near the Eiffel tower&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Gemini might answer something along the lines of this
(with details coming from Maps, as star rating proves, not from its training knowledge):</p>
<blockquote>
<p>For a memorable dining experience near the Eiffel Tower, several
highly-rated restaurants offer a range of cuisines and atmospheres.</p>
<p><strong>French Cuisine with a View:</strong>
For an exceptional meal with stunning views, consider <strong>Jules Verne</strong>,
an elegant restaurant located within the Eiffel Tower itself,
boasting a 4.5-star rating. Another option is <strong>Francette</strong>,
a refined restaurant on a barge on the Seine, also with a 4.5-star
rating and direct views of the tower.</p>
<p><strong>Café de l’Homme</strong> is a stylish bistro in the Musée de l’Homme,
featuring outdoor tables with Eiffel Tower views and a 4.1-star
rating.</p>
<p><strong>Top-Rated French Bistros:</strong>
If a classic French bistro is more your style, <strong>De la Tour</strong> is
a popular family-run establishment with a 4.8-star rating.
<strong>Arnaud Nicolas</strong>, known for its artfully presented tasting menus,
has a 4.7-star rating. With a 4.8-star rating, <strong>Milagro</strong> is another
excellent choice. Also highly rated is <strong>Le CasseNoix</strong>, a charming,
retro spot with a 4.7-star rating.</p>
<p><strong>Italian Options:</strong>
If you&rsquo;re in the mood for Italian food, <strong>Chez Pippo</strong> is a cozy
trattoria with a 4.6-star rating. <strong>La Casa di Alfio</strong> is another
popular choice with a 4.5-star rating. With a 4.7-star rating,
<strong>In Casa</strong> is also nearby. And <strong>Pink Mamma</strong> has an impressive
4.7-star rating with over 45,000 reviews.</p></blockquote>
<h2 id="google-search-grounding">Google Search Grounding</h2>
<p>Standard text generation can now be grounded using Google Search, ensuring responses are based on up-to-date web information.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>chatModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-3-flash-preview&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">allowGoogleSearch</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// Enable Google Search tool</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>chatModel.<span style="color:#4070a0">chat</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;What are the latest models from OpenAI, Anthropic, and Google?&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="url-context-tool">URL Context Tool</h2>
<p>This feature allows the model to access and use information directly from specific URLs provided in the prompt context.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>chatModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-3-flash-preview&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">allowUrlContext</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// Enable URL Context tool</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// The model can now fetch and reason</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// over content from URLs in the prompt</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>chatModel.<span style="color:#4070a0">chat</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Check Guillaume Laforge&#39;s blog archive at
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    https://glaforge.dev/archive/
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    and tell me how many articles he wrote in January 2026
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Suffice to have URLs in your prompt.
<strong>No need to fetch or scrape</strong> the content yourself ahead of making the LLM call.</p>
<h2 id="multimodal-agents-image-generation">Multimodal Agents (Image Generation)</h2>
<p><code>AiServices</code> now supports returning generated images directly, enabling the creation of multimodal agents that can produce visual content.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">interface</span> <span style="color:#0e84b5;font-weight:bold">CreativeAssistant</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@UserMessage</span>(<span style="color:#4070a0">&#34;Generate a high-quality image of {{description}}&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>ImageContent<span style="color:#bbb"> </span><span style="color:#06287e">generateArtwork</span>(<span style="color:#555;font-weight:bold">@V</span>(<span style="color:#4070a0">&#34;description&#34;</span>)<span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>description);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>CreativeAssistant<span style="color:#bbb"> </span>assistant<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>AiServices<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">builder</span>(CreativeAssistant.<span style="color:#4070a0">class</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">chatModel</span>(GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-3-pro-image-preview&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ImageContent<span style="color:#bbb"> </span>artwork<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>assistant.<span style="color:#4070a0">generateArtwork</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;a cyberpunk street food stall&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Then you can retrieve the image via <code>artwork.image().base64data()</code> and save it.</p>

    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>In this example, we&rsquo;re using Nano Banana Pro!
Nano Banana is actually a chat model that has 2 response modalities:
text and images.</p>
<p>In the case of the <code>GeminiAiImageModel</code>, we were only requesting images to be generated. No text.</p>
      </div>
    </div><p><figure>
  <a href="#img-76e010b91e91660a94192c55dae861b3">
    <img src="/img/nano-banana/cyberpunk-food-stall.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-76e010b91e91660a94192c55dae861b3">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/nano-banana/cyberpunk-food-stall.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h2 id="gemini-30-thinking-configuration">Gemini 3.0 Thinking Configuration</h2>
<p>You can configure the <em>&ldquo;thinking&rdquo;</em> process (Chain-of-Thought) for Gemini 3.0 models, allowing you to control the depth of reasoning.</p>
<p>Thinking levels available: <code>MINIMAL</code>, <code>LOW</code>, <code>MEDIUM</code>, <code>HIGH</code>.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>thinkingModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-3-flash-preview&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">sendThinking</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">   </span><span style="color:#60a0b0;font-style:italic">// Send thinking process to the model</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">returnThinking</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// Return thought process in the response</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">thinkingConfig</span>(GeminiThinkingConfig.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">thinkingLevel</span>(GeminiThinkingLevel.<span style="color:#4070a0">HIGH</span>)<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// Reasoning depth</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>thinkingModel.<span style="color:#4070a0">chat</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;Solve this complex logic puzzle...&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="enhanced-metadata--token-usage">Enhanced Metadata &amp; Token Usage</h2>
<p>Responses from both Chat and Image models now include richer metadata,
including detailed token usage and grounding source information
(e.g., which web pages or map locations were used).</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ChatResponse<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>chatModel.<span style="color:#4070a0">chat</span>(request);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Cast to the Gemini specific response type</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// to get access to the metadata provided by Gemini</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>GoogleAiGeminiChatResponseMetadata<span style="color:#bbb"> </span>metadata<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>(GoogleAiGeminiChatResponseMetadata)<span style="color:#bbb"> </span>response.<span style="color:#4070a0">metadata</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Access Grounding Metadata</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(metadata.<span style="color:#4070a0">groundingMetadata</span>()<span style="color:#bbb"> </span><span style="color:#666">!=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>metadata.<span style="color:#4070a0">groundingMetadata</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">groundingChunks</span>().<span style="color:#4070a0">forEach</span>(chunk<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(chunk.<span style="color:#4070a0">web</span>()<span style="color:#bbb"> </span><span style="color:#666">!=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;Source: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>chunk.<span style="color:#4070a0">web</span>().<span style="color:#4070a0">title</span>()<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34; (&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>chunk.<span style="color:#4070a0">web</span>().<span style="color:#4070a0">uri</span>()<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;)&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>});<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="summary">Summary</h2>
<p>These <strong>Gemini-related enhancements in LangChain4j 1.11.0</strong> further expand the capabilities of the Gemini integration.</p>
<p>From advanced <strong>image generation and editing with Nano Banana</strong> (Gemini 2.5 and 3.0 Preview)
to powerful <strong>grounding features with Google Search and Google Maps</strong>,
developers can now build more intelligent and context-aware applications.</p>
<p>The introduction of the <strong>URL Context tool</strong>, <strong>multimodal agents</strong>,
and <strong>configurable thinking processes</strong> for Gemini 3.0 allow for richer interactions and more precise control over model behavior.</p>
<p>The improved <strong>metadata and token usage reporting</strong> also provide valuable insights
for optimizing and understanding model responses.</p>
<p>Have fun with Gemini!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Researching Topics in the Age of AI — Rock-Solid Webhooks Case Study</title><link>https://glaforge.dev/posts/2026/02/04/researching-topics-in-the-age-of-ai-rock-solid-webhooks-case-study/</link><pubDate>Wed, 04 Feb 2026 15:06:17 +0100</pubDate><guid>https://glaforge.dev/posts/2026/02/04/researching-topics-in-the-age-of-ai-rock-solid-webhooks-case-study/</guid><description>&lt;p>Back in 2019, I spent significant time researching &lt;strong>Webhooks&lt;/strong>.
In particular, I was interested in best practices, pitfalls, design patterns,
and approaches for implementing Webhooks in a reliable, resilient, and effective way.&lt;/p>
&lt;p>Everything is distilled in that article:
&lt;a href="https://glaforge.dev/talks/2019/11/25/implementing-webhooks-not-as-trivial-as-it-may-seem/">Implementing Webhooks, not as trivial as it may seem&lt;/a>&lt;/p>
&lt;p>It likely took me a full week to dive deep into this subject, finding sources and
experimenting with design patterns myself.
But nowadays, AI makes it easier to dive deeper into topics, explore unfamiliar aspects,
and share findings with your team.&lt;/p></description><content:encoded>
<![CDATA[<p>Back in 2019, I spent significant time researching <strong>Webhooks</strong>.
In particular, I was interested in best practices, pitfalls, design patterns,
and approaches for implementing Webhooks in a reliable, resilient, and effective way.</p>
<p>Everything is distilled in that article:
<a href="https://glaforge.dev/talks/2019/11/25/implementing-webhooks-not-as-trivial-as-it-may-seem/">Implementing Webhooks, not as trivial as it may seem</a></p>
<p>It likely took me a full week to dive deep into this subject, finding sources and
experimenting with design patterns myself.
But nowadays, AI makes it easier to dive deeper into topics, explore unfamiliar aspects,
and share findings with your team.</p>
<p>As I built a research agent based on Google&rsquo;s <a href="https://ai.google.dev/gemini-api/docs/deep-research">Deep Research agent</a>,
I wanted to see how far it&rsquo;d go with a topic I had covered a while ago.</p>
<p>Armed with my <a href="https://glaforge.dev/posts/2026/01/03/building-a-research-assistant-with-the-interactions-api-in-java/">custom research agent</a>
(via the <a href="/posts/2026/01/30/a-javelit-frontend-for-the-deep-research-agent.md">Javelit frontend</a> I built around it),
I entered the query: <code>Webhook best practices for rock solid and resilient deployments</code>.</p>
<p>Gemini 3 Flash gave me a list of topics associated with that theme, and I selected the following ones:</p>
<blockquote>
<ul>
<li>Cryptographic signature verification using HMAC-SHA256</li>
<li>Implementing idempotency keys to prevent duplicate event processing</li>
<li>Asynchronous processing architectures using message queues and background workers</li>
<li>Preventing replay attacks with timestamp validation and nonces</li>
<li>Retry strategies using exponential backoff with jitter</li>
<li>Dead letter queue (DLQ) implementation and management for failed deliveries</li>
<li>Security through Mutual TLS (mTLS) and IP allowlisting</li>
<li>Webhook payload versioning and backward compatibility strategies</li>
<li>Handling high-volume event bursts with rate limiting and buffering</li>
<li>Circuit breaker patterns to protect downstream services from failure</li>
<li>Schema validation and data minimization in webhook payloads</li>
</ul></blockquote>
<p>There&rsquo;s significant overlap with the topics I covered in my old presentation!
I talked about idempotency, signatures, dead-letter queues, IP allowlisting, rate limiting &amp; buffering, etc.
This validates my previous findings and shows that today&rsquo;s generative AI
capabilities can identify the same key topics.</p>
<p>So I let Deep Research explore those topics.
After a few minutes, my agent wrote a long report in a concise style
(I used prompts to steer the report generation toward concise writing and avoid &ldquo;AI Slop&rdquo;).
Then Gemini 3 Pro created a summary, and Nano Banana Pro generated a sketchnote illustration.</p>
<p>Let&rsquo;s start with the illustration (the same you saw as the banner for this post):</p>
<h2 id="infographics">Infographics</h2>
<p><figure>
  <a href="#img-87b43c549c5fbb8f70335e3dc246c199">
    <img src="/img/gemini/webhooks-sketchnote.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-87b43c549c5fbb8f70335e3dc246c199">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/webhooks-sketchnote.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I have a special prompt to generate such sketchnotes, which I find readable, and appealing to share on social media:</p>
<pre tabindex="0"><code>Create a hand-drawn and hand-written sketchnote style
summary infographic, with a pure white background,
use fluo highlighters for the key points,
about the following information: ...
</code></pre><p>And I pass the summary generated by Gemini 3 Pro below.</p>
<h2 id="summary">Summary</h2>
<p>The summary is generated via this prompt, passing the entire research:</p>
<pre tabindex="0"><code>Create a concise summary of the research below.
Go straight to the summary; don&#39;t introduce it
(don&#39;t write &#34;Here&#39;s a summary...&#34; or equivalent).
</code></pre><p>For this research on Webhooks, here&rsquo;s what the summary was.
The summary&rsquo;s structure also guided the organization of the sketchnote above:</p>
<blockquote>
<p>Webhooks facilitate real-time, event-driven communication between systems but require a defensive architecture to ensure security, reliability, and scalability. Unlike polling, webhooks utilize a &ldquo;push&rdquo; model, necessitating robust handling of network partitions, malicious activity, and traffic spikes.</p>
<p><strong>Security and Authentication</strong> Security implementation must go beyond obscured URLs. The industry standard involves <strong>HMAC-SHA256 signature verification</strong> to ensure payload integrity and authenticity. Critical implementation details include using constant-time string comparisons to prevent timing attacks and validating raw, unparsed payloads. To prevent replay attacks, systems should enforce timestamp tolerance windows and utilize nonces. While Mutual TLS (mTLS) offers a higher security standard for zero-trust environments, it introduces significant complexity compared to signatures and IP allowlisting.</p>
<p><strong>Reliability and Architecture</strong> Because webhooks typically guarantee &ldquo;at-least-once&rdquo; delivery, receivers must implement <strong>idempotency</strong> using unique event keys and atomic storage to prevent duplicate processing from corrupting data. To handle high throughput and avoid timeouts, architectures should be asynchronous: an ingestion layer should immediately acknowledge requests (returning 202 Accepted) and offload the payload to a message queue for background processing by workers.</p>
<p><strong>Failure Handling and Recovery</strong> Robust systems employ <strong>exponential backoff with jitter</strong> for retries to prevent &ldquo;thundering herd&rdquo; scenarios that could overwhelm the receiver. Messages that fail all retry attempts should be routed to a <strong>Dead Letter Queue (DLQ)</strong> for inspection and potential redrive rather than being discarded. Additionally, <strong>circuit breakers</strong> are essential to pause delivery to failing endpoints, protecting the infrastructure from cascading failures during outages.</p>
<p><strong>Scalability and Payload Design</strong> To manage bursty traffic, providers should enforce rate limiting and buffering. Payload design involves a trade-off between &ldquo;Fat&rdquo; payloads (full state, convenient but larger attack surface) and &ldquo;Thin&rdquo; payloads (notifications only, secure but require callback API calls). Best practices suggest keeping payloads under 20kb, minimizing PII, and utilizing additive versioning per event type to maintain backward compatibility.</p></blockquote>
<p>Even <em>just</em> the summary, as is, is already valuable when researching a subject, as it gives you all the key pointers to go further.</p>
<h2 id="generating-a-slide-deck">Generating a Slide Deck</h2>
<p>I delivered my Webhook talk at a handful of conferences in Europe.
And of course, I needed a slide deck!
I often spend significant time on my decks, as I want them to be polished, clear, and beautiful.
Usually, I try to reduce the amount of text in favor of visual representations like diagrams and photographic illustrations.</p>
<p>Right, so I have a report and an infographic, but what about the deck?
My agent doesn&rsquo;t (yet?) handle that, so instead, I turned to <a href="https://notebooklm.google.com/">NotebookLM</a>,
gave it my research, and asked it to generate a slide deck with a particular design:</p>
<blockquote>
<p>A slide deck for a technical audience, describing all the best practices to implement rock solid resilient webhooks.
Opt for a blueprint architectural style, with illustrations.</p></blockquote>
<p>And it complied, generating the following deck, which I could see myself presenting:</p>
<script async class="speakerdeck-embed" data-id="1e129493512f4eb6bf05cbaad05ec4ad" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>The style is consistent across the deck and quite beautiful.
I usually put less text on slides, but it works here.
Under the hood, NotebookLM uses &#x1f34c; <strong>Nano Banana Pro</strong> (aka Gemini 3 Pro Image),
and the included graphics look spot on, sharp, and accurate.
I didn&rsquo;t even spot typos in the generated text.</p>
<h2 id="going-further-should-i-share-those">Going Further: Should I share those?</h2>
<p>More and more I use AI in my work, but I still prefer writing my articles by hand.
I can use generative AI to do a first draft, explain a piece of code, or come up with a conclusion.
I also use image generation to create either illustrations or sketchnotes.
But otherwise, it&rsquo;s still me. My writing, my design, my style, my authentic voice.</p>
<p>What I&rsquo;m wondering though is what to do with such research reports.
I run such reports to explore a particular topic, to avoid forgetting some key angle or aspect.
But I usually keep that research for me (sometimes saved inside my <a href="https://obsidian.md/">Obsidian</a> vault, or as Google Docs here and there)</p>
<p>This week, I had a nice lunch with a couple old friends who were thinking that it was <strong>worth sharing those reports</strong> more widely, rather than keeping them private
(one of my friend was sharing his research publicly in a GitHub repository.)</p>
<p>On the one hand, I don&rsquo;t want to <strong>increase the quantity of AI slop available on the internet</strong>,
but on the other hand, once they&rsquo;re generated, it&rsquo;s sad to see all those tokens wasted, and benefiting me exclusively!
I would clearly label them as <em>AI generated research reports</em> or similar though, but maybe others would find those useful and interesting?</p>
<p>I&rsquo;d be <strong>curious to hear your thoughts</strong> on this!
Don&rsquo;t hesitate to share them with me on social media.</p>
<p>Generative AI, assisted with agents that do targeted web searches, really changes the game in terms of research.
Tools like <a href="https://notebooklm.google.com/">NotebookLM</a> are able to find the right sources of information, and can generate all sorts of artifacts and visualisations
(audio and video podcasts, mindmaps, infographics, quizes, etc.)
And image models like &#x1f34c; <strong>Nano Banana</strong> are incredible and able to generate very clear visuals.
This is really an interesting era to learn more about any topics, and at a much greater depth than scouring your favorite search engine manually!
Deep Research and NotebookLM give you the URLs of the sources, so you can double check the accuracy of the reference material.</p>
<p>For the curious, here&rsquo;s the full research report below, that my agent crafted about rock-solid webhooks:</p>

<details>
  <summary>Click to view the full generated report on Webhooks</summary>
  <h2 id="webhook-implementation-best-practices-security-reliability-and-scalability">Webhook Implementation Best Practices: Security, Reliability, and Scalability</h2>
<h3 id="key-points"><strong>Key Points</strong></h3>
<ul>
<li><strong>Security is Paramount:</strong> Relying solely on obscure URLs is insufficient; implementation must include cryptographic signing (HMAC-SHA256) to ensure integrity and authenticity, alongside HTTPS for confidentiality.</li>
<li><strong>Reliability through Idempotency:</strong> Because webhooks typically guarantee &ldquo;at-least-once&rdquo; delivery, receivers must implement idempotency keys to safely handle duplicate requests without corrupting data state.</li>
<li><strong>Asynchronous Architecture:</strong> Decoupling ingestion from processing using message queues is critical for handling traffic bursts and preventing timeout failures at the ingress point.</li>
<li><strong>Failure Mitigation:</strong> Robust systems employ exponential backoff with jitter for retries to prevent &ldquo;thundering herd&rdquo; scenarios, utilizing Dead Letter Queues (DLQs) for effectively managing permanently failed messages.</li>
<li><strong>Traffic Management:</strong> Circuit breakers and rate limiting are essential to protect both the sender and receiver infrastructure from cascading failures during high-load events or outages.</li>
</ul>
<h3 id="introduction"><strong>Introduction</strong></h3>
<p>Webhooks represent the standard for event-driven communication between distributed systems, allowing platforms to notify downstream services of state changes in real-time. Unlike polling, which is resource-intensive and suffers from latency, webhooks enable a &ldquo;push&rdquo; model where data is transmitted immediately upon event occurrence. However, this architectural shift introduces significant challenges regarding security, reliability, and scalability. A production-grade webhook implementation requires a rigorous adherence to defensive design patterns to handle network partition, malicious actors, and extreme volume spikes.</p>
<p>This report synthesizes best practices across security verification, architectural decoupling, failure recovery, and payload design. It serves as a comprehensive guide for engineering teams aiming to build or consume resilient webhook systems that function reliably at scale.</p>
<h4 id="1-cryptographic-security-and-authentication"><strong>1. Cryptographic Security and Authentication</strong></h4>
<p>The public exposure of webhook endpoints makes them susceptible to impersonation, tampering, and man-in-the-middle attacks. Security must be implemented in layers, primarily focusing on transport security and payload verification.</p>
<h5 id="11-hmac-sha256-signature-verification"><strong>1.1 HMAC-SHA256 Signature Verification</strong></h5>
<p>The industry standard for authenticating webhook payloads is the Hash-based Message Authentication Code (HMAC) using the SHA-256 algorithm. This mechanism allows the receiver to verify that the payload was generated by the expected sender and has not been modified in transit <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEQCXOGuJl7Dh-VhImsadFbEWJxadTB33mDMPL2zHVmbHLv1IfGzFfSEOPmNZolZ00UFxk13zMvSO9B2llkj2RvC8tHuUF3bSc8c9KRD4Ww0yIH_1EbuKPDj03sOz5Y0VsLzTJPPh1TVzK4n5l8kLgSZmrmFROXpNMvIxvs5rjDIr2lIYRLaEUpgcM6uqk=">1</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGapOwdbfMw2xiKf7LQTypi27arEInXlcwMSM0OvS9Irikt1PHqfjKo7eYX1-GSXVUnL1OZKvJPBglA0qm3gwyUNEteJbfdTzPs3rGU1u9KFaVgnecU31VCHI9sAoLW6216rF5E_DmHOhb7g2DP-SfhA7Nv6OxoS08HZEaK-XGw3LBF5NG7W8n4kww_Iw8j6DXuhqLF3g==">2</a>.</p>
<p><strong>Mechanism of Action:</strong></p>
<ol>
<li><strong>Shared Secret:</strong> A secret key is exchanged between the webhook provider and the consumer. This key is never transmitted over the network but is stored securely (e.g., in a Key Management Service) <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGapOwdbfMw2xiKf7LQTypi27arEInXlcwMSM0OvS9Irikt1PHqfjKo7eYX1-GSXVUnL1OZKvJPBglA0qm3gwyUNEteJbfdTzPs3rGU1u9KFaVgnecU31VCHI9sAoLW6216rF5E_DmHOhb7g2DP-SfhA7Nv6OxoS08HZEaK-XGw3LBF5NG7W8n4kww_Iw8j6DXuhqLF3g==">2</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHtLMqFzEPWWTqr57hgCAajV8f2Ij_c5UJeD5WwDVNpUV3KinmSkEGvoP7IlL0h77x5SOjddKsCURFSbuoHB9N0fMQEuZwA6vaxQ3RQb8C4dnCsp7Y0pcosupydpCz5fIJL0A-J8BadBDgNFkVG_TUiGKVx6nm4T8D5sPbuTg==">3</a>.</li>
<li><strong>Hashing:</strong> The provider computes a hash of the payload body using the shared secret and the SHA-256 algorithm.</li>
<li><strong>Transmission:</strong> This hash is included in the HTTP headers (e.g., X-Signature or X-Hub-Signature-256) sent with the POST request <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFS-EcH1LqHZM6UuNXeP-qitgt8WWv5rnN9tweCAL1n2Ek29RI_lGbpCGS6GSkPw2HsuOwmoxLEfSQB5p03WIGFpYueJRELK05i8aXPElmhgw9Wnxt9geuz9jBrOzWvRNiUC2Jtfh0vM0lO2-uzvqU=">4</a>.</li>
<li><strong>Verification:</strong> Upon receipt, the consumer independently computes the hash of the raw payload using their copy of the secret and compares it to the header value <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEXs9GPnNDFPvttHxuwH1fmRNaG7g0AEVP78LNYuD72y5prRsjxAWF1SJ7WgvtvtIU8dmVKOPBXtRWsieTrZQMM7OXct27gnZKRZAdfgsbQTPepbGNrXnoGvjH2hSqBMbdh04kEeOZmgZ3qyw==">5</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH12GnFNNVZoTt8EZ7ZELuQ2jyM2MTTg6fsUKd1Qv0j0xbJyJXE6P5wB89sRaqCHdCdkRPtra8E2C_-lrPXWn96F_s1PpwosEeiwqalYPXcS57HDounBKc5L1EXkPBEtg==">6</a>.</li>
</ol>
<p><strong>Implementation Criticalities:</strong></p>
<ul>
<li><strong>Constant-Time Comparison:</strong> When comparing the calculated signature with the received signature, developers must use a constant-time string comparison function. Standard string comparison returns false as soon as a mismatch is found, which exposes the system to timing attacks where an attacker can deduce the signature character by character based on response time <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHtLMqFzEPWWTqr57hgCAajV8f2Ij_c5UJeD5WwDVNpUV3KinmSkEGvoP7IlL0h77x5SOjddKsCURFSbuoHB9N0fMQEuZwA6vaxQ3RQb8C4dnCsp7Y0pcosupydpCz5fIJL0A-J8BadBDgNFkVG_TUiGKVx6nm4T8D5sPbuTg==">3</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH12GnFNNVZoTt8EZ7ZELuQ2jyM2MTTg6fsUKd1Qv0j0xbJyJXE6P5wB89sRaqCHdCdkRPtra8E2C_-lrPXWn96F_s1PpwosEeiwqalYPXcS57HDounBKc5L1EXkPBEtg==">6</a>.</li>
<li><strong>Raw Payload Access:</strong> Verification must be performed on the raw, unparsed request body. Frameworks that automatically parse JSON before verification can alter whitespace or field ordering, causing hash mismatches <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHtLMqFzEPWWTqr57hgCAajV8f2Ij_c5UJeD5WwDVNpUV3KinmSkEGvoP7IlL0h77x5SOjddKsCURFSbuoHB9N0fMQEuZwA6vaxQ3RQb8C4dnCsp7Y0pcosupydpCz5fIJL0A-J8BadBDgNFkVG_TUiGKVx6nm4T8D5sPbuTg==">3</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH12GnFNNVZoTt8EZ7ZELuQ2jyM2MTTg6fsUKd1Qv0j0xbJyJXE6P5wB89sRaqCHdCdkRPtra8E2C_-lrPXWn96F_s1PpwosEeiwqalYPXcS57HDounBKc5L1EXkPBEtg==">6</a>.</li>
<li><strong>Key Rotation:</strong> Security best practices dictate the ability to rotate secrets without downtime. This is achieved by supporting multiple active keys during a transition period or using a Key-ID header to indicate which secret was used for signing <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEQCXOGuJl7Dh-VhImsadFbEWJxadTB33mDMPL2zHVmbHLv1IfGzFfSEOPmNZolZ00UFxk13zMvSO9B2llkj2RvC8tHuUF3bSc8c9KRD4Ww0yIH_1EbuKPDj03sOz5Y0VsLzTJPPh1TVzK4n5l8kLgSZmrmFROXpNMvIxvs5rjDIr2lIYRLaEUpgcM6uqk=">1</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGapOwdbfMw2xiKf7LQTypi27arEInXlcwMSM0OvS9Irikt1PHqfjKo7eYX1-GSXVUnL1OZKvJPBglA0qm3gwyUNEteJbfdTzPs3rGU1u9KFaVgnecU31VCHI9sAoLW6216rF5E_DmHOhb7g2DP-SfhA7Nv6OxoS08HZEaK-XGw3LBF5NG7W8n4kww_Iw8j6DXuhqLF3g==">2</a>.</li>
</ul>
<h4 id="12-mutual-tls-mtls"><strong>1.2 Mutual TLS (mTLS)</strong></h4>
<p>While HTTPS ensures encryption in transit, Mutual TLS (mTLS) provides a higher level of authentication by requiring both the client (webhook sender) and the server (webhook receiver) to present valid x.509 certificates during the TLS handshake <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFS-EcH1LqHZM6UuNXeP-qitgt8WWv5rnN9tweCAL1n2Ek29RI_lGbpCGS6GSkPw2HsuOwmoxLEfSQB5p03WIGFpYueJRELK05i8aXPElmhgw9Wnxt9geuz9jBrOzWvRNiUC2Jtfh0vM0lO2-uzvqU=">4</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE3k1o-3NRkMcffQX8rtbkeOZtI6fj-vyPodgDETt437-jH_lOLx0vKahj92Vaxo28UE8xXjEAAigXWq6jNmbJysY_GhRMoQY9vw_gREej2hXsZ4atFoO-NPyd9pmC9cQkh3VEvU8MCvS8=">7</a>.</p>
<p><strong>Advantages and Trade-offs:</strong> mTLS aligns with Zero Trust security principles, ensuring that traffic is trusted in both directions at the transport layer before any application logic is invoked <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE3k1o-3NRkMcffQX8rtbkeOZtI6fj-vyPodgDETt437-jH_lOLx0vKahj92Vaxo28UE8xXjEAAigXWq6jNmbJysY_GhRMoQY9vw_gREej2hXsZ4atFoO-NPyd9pmC9cQkh3VEvU8MCvS8=">7</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFXhbKv9jWGpppHz-I9OAOAkGHbvPQGssu-IzoCcTn7-AJvw_KRC0mtAWzp0ML2hS1UkoasxuhHfOCySlYdoFKmGGbOqw2usCXYiQfNz2wzBJ-46HpNCfh_OiX64LsO2iBrv-nH9tDfEZhTYUlPnjQwmolyARu3jSigFIC8KYykE04XjV8ccUWP_CLCz6tVC516k0WI2SfUEoFzX1uQhE6xJa4rDEq6JhURZuEt7rhKDNqURA==">8</a>. It effectively mitigates spoofing and man-in-the-middle attacks. However, it introduces significant operational complexity regarding certificate management, issuance, and rotation <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE3k1o-3NRkMcffQX8rtbkeOZtI6fj-vyPodgDETt437-jH_lOLx0vKahj92Vaxo28UE8xXjEAAigXWq6jNmbJysY_GhRMoQY9vw_gREej2hXsZ4atFoO-NPyd9pmC9cQkh3VEvU8MCvS8=">7</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFXhbKv9jWGpppHz-I9OAOAkGHbvPQGssu-IzoCcTn7-AJvw_KRC0mtAWzp0ML2hS1UkoasxuhHfOCySlYdoFKmGGbOqw2usCXYiQfNz2wzBJ-46HpNCfh_OiX64LsO2iBrv-nH9tDfEZhTYUlPnjQwmolyARu3jSigFIC8KYykE04XjV8ccUWP_CLCz6tVC516k0WI2SfUEoFzX1uQhE6xJa4rDEq6JhURZuEt7rhKDNqURA==">8</a>. For many use cases, mTLS is considered overkill compared to HMAC, but it remains the gold standard for high-security environments like banking or healthcare <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFXhbKv9jWGpppHz-I9OAOAkGHbvPQGssu-IzoCcTn7-AJvw_KRC0mtAWzp0ML2hS1UkoasxuhHfOCySlYdoFKmGGbOqw2usCXYiQfNz2wzBJ-46HpNCfh_OiX64LsO2iBrv-nH9tDfEZhTYUlPnjQwmolyARu3jSigFIC8KYykE04XjV8ccUWP_CLCz6tVC516k0WI2SfUEoFzX1uQhE6xJa4rDEq6JhURZuEt7rhKDNqURA==">8</a>.</p>
<h4 id="13-ip-allowlisting"><strong>1.3 IP Allowlisting</strong></h4>
<p>Restricting webhook traffic to a specific list of IP addresses (allowlisting) is a common defense-in-depth strategy. By blocking all traffic not originating from known provider IPs, the attack surface is reduced <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEDIwllRPtSE_Pf62YeXgIYgsDEfs-1wHuv9ezOKwFYSaEc39N4e_yAOhbtRX1t-lLnt_VqbdnEsbq1uSII6uRxqit-ZihDjM4StrhiNYPplF_yDLSKzglCmQg_JtAsbsmocQQU6WsYQo0kdLodcetTYvUPbQWjEMuZcovNsV0IvMRm4JFcWykx9BIBvrIMKg999w==">9</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGImss29WCJrZ-7A1qRvtpSXx2770lcn-FKzrohjNSvahorZqWNKElMcdM8ncv0_woeJHwVb1-xgukNmufkutg13vEv6wXuSWlpA5Xez7xFPYWUO9B1_c1B645Bj1Tfjmr8KEQr8A==">10</a>.</p>
<p><strong>Limitations in Modern Architectures:</strong> IP allowlisting is increasingly difficult to maintain in cloud-native environments where providers use dynamic IP ranges or serverless infrastructure <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFYYHzohwba_5v9CJiFs8mD1iz9fbhE8MtwfSvmrUpiSIuWRvl2kvOSLUz9Uvjk6_5n-fLTAnIyoWJe2zh6vCb1i0m2bmHtT2vlK_jU2o-aPA0hlfcOtinI7exwqNXqj9Vrv1Oq6--c0TPo-NGY23eKvhemf-YeZ6UQWTITLFB1oWHphtQzzR2dfuIB4IYT3URzUbF8VVUosF8=">11</a>. It creates a maintenance burden where the consumer must manually update firewall rules whenever the provider expands their infrastructure <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHyAw6S_aOVlsVoLFQg0ZMCQlWmrBgVHp4dBnn2xttREglexTIahmODOAstaUnlwsNIP87xlAcCBSasuOixIkwZ6P5b6tzO9rqx4rahyPJ98wCyFn9P_A3mpjCOu7wNm_xYBANICeyhNA==">12</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH1xca3J3HdA9cCG1LqCARxi2f2drvrB3kvAEkgwonEY73ciX1Lfc37H71xRSwdAwybTc7UIK6pLHVBmlgaa5oQG-hkozuAkTN_ESLLLgMFL3uXCe_YKsU4QDCfxWE9V8YUG8_9iVClMHZsmwa0Ny7ZUUTFq_7IPBGSW85ourPVJ9I=">13</a>. Consequently, IP allowlisting should be treated as a supplementary measure rather than a primary authentication method <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHtLMqFzEPWWTqr57hgCAajV8f2Ij_c5UJeD5WwDVNpUV3KinmSkEGvoP7IlL0h77x5SOjddKsCURFSbuoHB9N0fMQEuZwA6vaxQ3RQb8C4dnCsp7Y0pcosupydpCz5fIJL0A-J8BadBDgNFkVG_TUiGKVx6nm4T8D5sPbuTg==">3</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH1xca3J3HdA9cCG1LqCARxi2f2drvrB3kvAEkgwonEY73ciX1Lfc37H71xRSwdAwybTc7UIK6pLHVBmlgaa5oQG-hkozuAkTN_ESLLLgMFL3uXCe_YKsU4QDCfxWE9V8YUG8_9iVClMHZsmwa0Ny7ZUUTFq_7IPBGSW85ourPVJ9I=">13</a>.</p>
<h4 id="14-preventing-replay-attacks"><strong>1.4 Preventing Replay Attacks</strong></h4>
<p>A replay attack occurs when an attacker intercepts a valid, signed webhook request and resends it to the endpoint to duplicate an action (e.g., forcing a second payment).</p>
<p><strong>Timestamp Validation:</strong> To prevent this, the signature header should include a timestamp. The receiver verifies that the timestamp is within a strictly defined tolerance window (e.g., 5 minutes) relative to the system time <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH12GnFNNVZoTt8EZ7ZELuQ2jyM2MTTg6fsUKd1Qv0j0xbJyJXE6P5wB89sRaqCHdCdkRPtra8E2C_-lrPXWn96F_s1PpwosEeiwqalYPXcS57HDounBKc5L1EXkPBEtg==">6</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE6mQdSQzonHQsRQLG1x2iQbAc14h1b0QyfDZPE1MsFLTlnRODkc6cJqhFJfn5sTtonCXscipnJQhEOtW1TgQoZt9bJ5wG-o8XJY9hWEw2iHJOXWDuFMxbviQLx5h-_dgoXF-Op">14</a>. If the request is too old, it is rejected, even if the signature is valid. Including the timestamp in the signed payload ensures the attacker cannot modify the time to bypass the check <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE6mQdSQzonHQsRQLG1x2iQbAc14h1b0QyfDZPE1MsFLTlnRODkc6cJqhFJfn5sTtonCXscipnJQhEOtW1TgQoZt9bJ5wG-o8XJY9hWEw2iHJOXWDuFMxbviQLx5h-_dgoXF-Op">14</a>.</p>
<p><strong>Nonce Implementation:</strong> For stronger protection, a unique &ldquo;nonce&rdquo; (number used once) or unique request ID can be included. The receiver stores processed nonces in a fast lookup store (like Redis) with a Time-To-Live (TTL) matching the replay window. If a nonce is seen a second time, the request is rejected <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH12GnFNNVZoTt8EZ7ZELuQ2jyM2MTTg6fsUKd1Qv0j0xbJyJXE6P5wB89sRaqCHdCdkRPtra8E2C_-lrPXWn96F_s1PpwosEeiwqalYPXcS57HDounBKc5L1EXkPBEtg==">6</a>.</p>
<h3 id="2-reliability-and-data-integrity"><strong>2. Reliability and Data Integrity</strong></h3>
<p>Distributed systems cannot guarantee &ldquo;exactly-once&rdquo; delivery due to network acknowledgments potentially failing after processing. Therefore, webhook systems almost universally operate on an &ldquo;at-least-once&rdquo; delivery model, necessitating robust handling of duplicate events.</p>
<h4 id="21-idempotency-implementation"><strong>2.1 Idempotency Implementation</strong></h4>
<p>Idempotency ensures that performing the same operation multiple times produces the same result as performing it once. This is the primary defense against data corruption caused by webhook retries <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEQfVAEpbMLruPbjNGZ738eh_ylZHWHPnu_qZm9XTLHB-AT0BmDjUV_H1Lv00QMDQhFo9DgyxKxPUtoHpGCr0y7XlujSMPRVNqbFaRcmSCOGLV78IAM-ewJFRv_ttzj2JOp_tgD2udxcyAIsAUbcLhsS1uYoNKoJkI=">15</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEHXKe8AnCoBNUic4_dIw3dZ5D6GIcUTosndmVu1nzaiSfbBhtV04q7oMzc4l71nWdcRfuExdy1qe2cHZj_2E8QGFjwSWWlQthKKPP3YDgs9Wm5ZiXPAS6z7rAhkvllqe6Uw73rz9UGA1IPPZ_-RI4h124D0AocAIjiY798wYZn5_k0U_K1qAcdSG9jxHTwuFcaEEUz9g==">16</a>.</p>
<p><strong>Idempotency Keys:</strong> Providers should include a unique identifier (Idempotency Key or event_id) in the webhook headers or payload <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEHXKe8AnCoBNUic4_dIw3dZ5D6GIcUTosndmVu1nzaiSfbBhtV04q7oMzc4l71nWdcRfuExdy1qe2cHZj_2E8QGFjwSWWlQthKKPP3YDgs9Wm5ZiXPAS6z7rAhkvllqe6Uw73rz9UGA1IPPZ_-RI4h124D0AocAIjiY798wYZn5_k0U_K1qAcdSG9jxHTwuFcaEEUz9g==">16</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEATF-8ojdZKpkXPPqDJmcexidA8Fo4g8M1737OwRPdyZbSoXi0RKkhctGTp62J5PReRU1dihQLC-Rr9o32-K-Gy2KjqBWlMjZa1hW2qWQtCVPYiNMXB-p1jRIfT9xLvPQL84kZg3YwHlxDck22cDUqRP94Uc7d3W5HuZNc2A87PCmU8peDNqbza0zPKZmTQuFZ9gaJsw==">17</a>. The receiver uses this key to lock processing for that specific event.</p>
<ul>
<li><strong>Deduplication Store:</strong> A fast, atomic store (e.g., Redis) checks if the key has been processed. Using atomic operations like SETNX (Set if Not Exists) prevents race conditions where two parallel requests for the same event might both proceed <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEHXKe8AnCoBNUic4_dIw3dZ5D6GIcUTosndmVu1nzaiSfbBhtV04q7oMzc4l71nWdcRfuExdy1qe2cHZj_2E8QGFjwSWWlQthKKPP3YDgs9Wm5ZiXPAS6z7rAhkvllqe6Uw73rz9UGA1IPPZ_-RI4h124D0AocAIjiY798wYZn5_k0U_K1qAcdSG9jxHTwuFcaEEUz9g==">16</a>.</li>
<li><strong>Retention Window:</strong> The keys should be stored for a duration exceeding the maximum retry window of the provider (typically 24 to 72 hours) <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHMukyFGeJn3lzizl34s01hqUqLH39KV4C3rqM6KdUEdBXPX9w1RSuEFAKZT_UX0C1NZu8KL_-4t3UzO0_cg69Nb_JI_u4dk8GMpFAhLqOOqPO8FuD3hgDsb45rLo0v8o1ZlOeLBpF2ZcZ3KXE7CPR-5ErLgSbfb9k8mPvJXOnpxUg1xtJoa0ynC8XFF0c=">18</a>.</li>
<li><strong>Transactional Upserts:</strong> In database operations, using &ldquo;upsert&rdquo; logic (update if exists, insert if new) based on the unique event ID ensures consistency at the database level <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEHXKe8AnCoBNUic4_dIw3dZ5D6GIcUTosndmVu1nzaiSfbBhtV04q7oMzc4l71nWdcRfuExdy1qe2cHZj_2E8QGFjwSWWlQthKKPP3YDgs9Wm5ZiXPAS6z7rAhkvllqe6Uw73rz9UGA1IPPZ_-RI4h124D0AocAIjiY798wYZn5_k0U_K1qAcdSG9jxHTwuFcaEEUz9g==">16</a>.</li>
</ul>
<h4 id="22-asynchronous-processing-architectures"><strong>2.2 Asynchronous Processing Architectures</strong></h4>
<p>Synchronous processing of webhooks—where the receiver executes business logic before returning an HTTP response—is a major anti-pattern. It couples the provider&rsquo;s availability to the consumer&rsquo;s processing speed and risks timeouts <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEQfVAEpbMLruPbjNGZ738eh_ylZHWHPnu_qZm9XTLHB-AT0BmDjUV_H1Lv00QMDQhFo9DgyxKxPUtoHpGCr0y7XlujSMPRVNqbFaRcmSCOGLV78IAM-ewJFRv_ttzj2JOp_tgD2udxcyAIsAUbcLhsS1uYoNKoJkI=">15</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEHXKe8AnCoBNUic4_dIw3dZ5D6GIcUTosndmVu1nzaiSfbBhtV04q7oMzc4l71nWdcRfuExdy1qe2cHZj_2E8QGFjwSWWlQthKKPP3YDgs9Wm5ZiXPAS6z7rAhkvllqe6Uw73rz9UGA1IPPZ_-RI4h124D0AocAIjiY798wYZn5_k0U_K1qAcdSG9jxHTwuFcaEEUz9g==">16</a>.</p>
<p><strong>Queue-Based Decoupling:</strong> The recommended architecture involves an ingestion layer that does nothing but authenticate the request, push the payload to a message queue (e.g., RabbitMQ, Kafka, SQS), and immediately return a 202 Accepted status <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHdp9hSU08oYMf-4GdNFGBYU4v4d93LvZ1d_ICLNnJcPAG2fUTOKHohQ-kOnJsaA6Teu2FI8Ut30grgFQBpXeaydSjx9bUWXZoU_hSLg_aL73uSS9CsVmK0LFDKAtq3YG4D31h1Kuszk39-Lk3nu09_NajWdBOTQy-_M8QxfRrEU9fL6K4WYNU28bRd_69TmlowBm2krMq_IWLW-qzdtaPgV3zuPDnB1w==">19</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH0JVPUOmptuBYlB8-wJwyjeuTAVRVyUb223KovQt8MsM-mlOtej6D_kcchgaC2ZwcjcEx0wq13bm76jEk23wIs0we5HpQ0QAgfz9A2FMmhmzNDMGbHA_jg0dMemVpSjrN_B-dQh6lKCPSh52kD7ND2Z6JdPrzXn_zdyqD3Tv4NZzOIHCfNK5myi6e-LOOUybc=">20</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH55diJUzcJR_u2AWR3Mr6S8HabBX1seyCwwOzAXTTU96fhqNP4RUhs06bklN1nNYw8eTQQd6K9c9NXUVwJ6kJN4y1CD8PMECf1LHUmz7X04825mo3sNLM72Er8DgB0tZ7SSlZl1-y2knwdSj8lxRwVWilPCBC9sGj3LHy_KBcphb5G6bcrehvsRDvgmioQzQepSjQOWAFn">21</a>.</p>
<ul>
<li><strong>Benefits:</strong> This ensures that the ingestion layer can handle high throughput without waiting for slow downstream processes (e.g., generating PDFs, sending emails) <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQED662YLvMnAViKnlF958nxB2BmJpS8SQlmVbMuE3Ko2pNQr0b3kaD7B_hyk1HUebgY3IcCs7tGCNRnInAdhxH-LtRvP6C-8byHujzYJChaayy4NZ_BaC3c4VkXU2dSYKuJ">22</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFd_CxUJPKeVMz1F02SqlVQjKBwFSscQ8mtFX755DcIRg6EU2j5-mjqOJu8hflle4-VnsqJbFMLbfTmn5fZUWuoq6RBBxRAlRKJWTYK0i9j9b7_ZTFlbVCRcc2DLxpvESUfrCiUPBj_a01pRXCaQ3lSo4v-5yuecvnWx6GAnnWg8M4vN4G1PnrSbZL-XcgBmsFqINYgUuI0b3yQR_BgMITXm7G1BCZ2Mm4=">23</a>.</li>
<li><strong>Worker Pattern:</strong> Background workers pull messages from the queue to process them. If a worker fails, the message remains in the queue or is moved to a retry queue, ensuring no data is lost during application crashes <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH0JVPUOmptuBYlB8-wJwyjeuTAVRVyUb223KovQt8MsM-mlOtej6D_kcchgaC2ZwcjcEx0wq13bm76jEk23wIs0we5HpQ0QAgfz9A2FMmhmzNDMGbHA_jg0dMemVpSjrN_B-dQh6lKCPSh52kD7ND2Z6JdPrzXn_zdyqD3Tv4NZzOIHCfNK5myi6e-LOOUybc=">20</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHJ3oedQbVxZag1bGXe54yIdze9Q9z8q0IPuWb3dPI47uWDHpqJz9cHmjP5-hZ6XYoQ8R-jYwG0hwIahcK4RbJwtYnyZElCsPNpqm4y1nzN-ClzqQ8DV2Fn9v69cFHr-NZnLfaKp7gy1fnIyHCorG7A-TaoWdCCR-6B5Y4CS0vP--U69OqbFvNGJnQB8klIbl36rFWVvvGG6A==">24</a>.</li>
<li><strong>Buffering:</strong> This architecture acts as a buffer (shock absorber) during traffic spikes, allowing the system to &ldquo;hold the load&rdquo; and process it at a manageable rate rather than crashing the web server <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFd_CxUJPKeVMz1F02SqlVQjKBwFSscQ8mtFX755DcIRg6EU2j5-mjqOJu8hflle4-VnsqJbFMLbfTmn5fZUWuoq6RBBxRAlRKJWTYK0i9j9b7_ZTFlbVCRcc2DLxpvESUfrCiUPBj_a01pRXCaQ3lSo4v-5yuecvnWx6GAnnWg8M4vN4G1PnrSbZL-XcgBmsFqINYgUuI0b3yQR_BgMITXm7G1BCZ2Mm4=">23</a>.</li>
</ul>
<h3 id="3-failure-handling-and-recovery"><strong>3. Failure Handling and Recovery</strong></h3>
<p>Failures in webhook delivery are inevitable due to network blips, downtime, or bugs. A robust system must distinguish between transient and permanent failures and handle each appropriately.</p>
<h5 id="31-retry-strategies-exponential-backoff-and-jitter"><strong>3.1 Retry Strategies: Exponential Backoff and Jitter</strong></h5>
<p>When a webhook delivery fails (e.g., receiver returns 500 or times out), the provider must retry. However, immediate retries can worsen the issue, especially if the receiver is overloaded.</p>
<p><strong>Exponential Backoff:</strong> This algorithm increases the wait time between retries exponentially (e.g., 1s, 2s, 4s, 8s). This gives the failing system &ldquo;breathing room&rdquo; to recover <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEkjQjNpTsb8JZ5qe59IFdmmAjGw8COOq_1pGnrUe37t1NDdlwblViRj7cSp7gccbNFGm1n5EQ1r6szF-x9YqTl4P5yKbnDbwfON9RSKjv3IwjZeKTSgVWmATqBkax3VdITfVgQKQpQD8GePx0=">25</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF99_r_zUtU_jYwFqB_uQJoJXExrO3kKFls8vi9mILvSgAUmdwy6FH_pE_OR796JSUEUJ-tYDqUfYDkStQBVIv7gcPbezN4RfgTSTIUFP-rXh24CgzRm3yr4JFMeqO4WX9_i2cUz6_765Rj7HRrVEEO7tWyRA==">26</a>.</p>
<ul>
<li><strong>Formula:</strong> 
  <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>D</mi><mi>e</mi><mi>l</mi><mi>a</mi><mi>y</mi><mo>=</mo><mi>B</mi><mi>a</mi><mi>s</mi><mi>e</mi><mo>×</mo><msup><mn>2</mn><mrow><mi>A</mi><mi>t</mi><mi>t</mi><mi>e</mi><mi>m</mi><mi>p</mi><mi>t</mi></mrow></msup></mrow><annotation encoding="application/x-tex">Delay = Base \times 2^{Attempt}</annotation></semantics></math></span>

 <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEkjQjNpTsb8JZ5qe59IFdmmAjGw8COOq_1pGnrUe37t1NDdlwblViRj7cSp7gccbNFGm1n5EQ1r6szF-x9YqTl4P5yKbnDbwfON9RSKjv3IwjZeKTSgVWmATqBkax3VdITfVgQKQpQD8GePx0=">25</a>.</li>
<li><strong>Capping:</strong> A maximum delay (e.g., 1 hour) prevents retry intervals from becoming unreasonably long <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEkjQjNpTsb8JZ5qe59IFdmmAjGw8COOq_1pGnrUe37t1NDdlwblViRj7cSp7gccbNFGm1n5EQ1r6szF-x9YqTl4P5yKbnDbwfON9RSKjv3IwjZeKTSgVWmATqBkax3VdITfVgQKQpQD8GePx0=">25</a>.</li>
</ul>
<p><strong>Jitter:</strong> Exponential backoff alone can lead to the &ldquo;Thundering Herd&rdquo; problem, where multiple failed webhooks retry at the exact same synchronized times, creating repeated spikes of traffic. &ldquo;Jitter&rdquo; adds randomness to the backoff interval to desynchronize these retries <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEkjQjNpTsb8JZ5qe59IFdmmAjGw8COOq_1pGnrUe37t1NDdlwblViRj7cSp7gccbNFGm1n5EQ1r6szF-x9YqTl4P5yKbnDbwfON9RSKjv3IwjZeKTSgVWmATqBkax3VdITfVgQKQpQD8GePx0=">25</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGrBXavT7Taw429hZQnfpdVUgMSqV0qPk3J1Kr1FYj9RI1REkA6gaHCgEcJpIE3pUUB5LOxm1mWgiZMr7bT1JsOLoNm7PO4TYhMMyLwJ0_HD95w9WrxiqJoLJVJJ4weqq7Xfaa1dOHz1gjxeHd8eNPeQRXEHsJFi6DwWKjldZa99ugI7ObRlXaXiAtlVPdAiY2I8L8pual1oHwnmbgajvVoMq1hSwplMf0ue9Y=">27</a>.</p>
<ul>
<li><strong>Full Jitter:</strong> 
  <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>D</mi><mi>e</mi><mi>l</mi><mi>a</mi><mi>y</mi><mo>=</mo><mi>r</mi><mi>a</mi><mi>n</mi><mi>d</mi><mi>o</mi><mi>m</mi><mo stretchy="false">(</mo><mn>0</mn><mo separator="true">,</mo><mi>B</mi><mi>a</mi><mi>s</mi><mi>e</mi><mo>×</mo><msup><mn>2</mn><mrow><mi>A</mi><mi>t</mi><mi>t</mi><mi>e</mi><mi>m</mi><mi>p</mi><mi>t</mi></mrow></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">Delay = random(0, Base \times 2^{Attempt})</annotation></semantics></math></span>

 is highly effective at spreading load <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHMukyFGeJn3lzizl34s01hqUqLH39KV4C3rqM6KdUEdBXPX9w1RSuEFAKZT_UX0C1NZu8KL_-4t3UzO0_cg69Nb_JI_u4dk8GMpFAhLqOOqPO8FuD3hgDsb45rLo0v8o1ZlOeLBpF2ZcZ3KXE7CPR-5ErLgSbfb9k8mPvJXOnpxUg1xtJoa0ynC8XFF0c=">18</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEkjQjNpTsb8JZ5qe59IFdmmAjGw8COOq_1pGnrUe37t1NDdlwblViRj7cSp7gccbNFGm1n5EQ1r6szF-x9YqTl4P5yKbnDbwfON9RSKjv3IwjZeKTSgVWmATqBkax3VdITfVgQKQpQD8GePx0=">25</a>.</li>
</ul>
<h4 id="32-dead-letter-queues-dlq"><strong>3.2 Dead Letter Queues (DLQ)</strong></h4>
<p>If a message fails to deliver after all retry attempts are exhausted, it should not be discarded. Instead, it must be moved to a Dead Letter Queue (DLQ) <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHdp9hSU08oYMf-4GdNFGBYU4v4d93LvZ1d_ICLNnJcPAG2fUTOKHohQ-kOnJsaA6Teu2FI8Ut30grgFQBpXeaydSjx9bUWXZoU_hSLg_aL73uSS9CsVmK0LFDKAtq3YG4D31h1Kuszk39-Lk3nu09_NajWdBOTQy-_M8QxfRrEU9fL6K4WYNU28bRd_69TmlowBm2krMq_IWLW-qzdtaPgV3zuPDnB1w==">19</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF99_r_zUtU_jYwFqB_uQJoJXExrO3kKFls8vi9mILvSgAUmdwy6FH_pE_OR796JSUEUJ-tYDqUfYDkStQBVIv7gcPbezN4RfgTSTIUFP-rXh24CgzRm3yr4JFMeqO4WX9_i2cUz6_765Rj7HRrVEEO7tWyRA==">26</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEW2f40SZCjfELqmNW1j9NyGNsCNsip2VIO51aLp1fUFClxVo6-KLqVo1OfM-Bevq9PGU_Kf_38maQ4Jmrdxqg7x_H5FmQ3dlUczpv5puiR6LdHG7CPYhZ14eWfHt7G8LHnwfjk_CCMF64KmU9bR5uoSNqShdc6ddPcjZW8_eOBw7M=">28</a>.</p>
<ul>
<li><strong>Purpose:</strong> The DLQ acts as a holding area for &ldquo;poison messages&rdquo; or permanently failed deliveries. This prevents the retry queue from being clogged with unprocessable events <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEATF-8ojdZKpkXPPqDJmcexidA8Fo4g8M1737OwRPdyZbSoXi0RKkhctGTp62J5PReRU1dihQLC-Rr9o32-K-Gy2KjqBWlMjZa1hW2qWQtCVPYiNMXB-p1jRIfT9xLvPQL84kZg3YwHlxDck22cDUqRP94Uc7d3W5HuZNc2A87PCmU8peDNqbza0zPKZmTQuFZ9gaJsw==">17</a>.</li>
<li><strong>Management:</strong> Systems must provide tooling to inspect DLQ messages, determine the root cause (e.g., bug in consumer code), and &ldquo;redrive&rdquo; (replay) them once the issue is fixed <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHMukyFGeJn3lzizl34s01hqUqLH39KV4C3rqM6KdUEdBXPX9w1RSuEFAKZT_UX0C1NZu8KL_-4t3UzO0_cg69Nb_JI_u4dk8GMpFAhLqOOqPO8FuD3hgDsb45rLo0v8o1ZlOeLBpF2ZcZ3KXE7CPR-5ErLgSbfb9k8mPvJXOnpxUg1xtJoa0ynC8XFF0c=">18</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHx0DNAVU9VSUpsBSRtjwACgynyVes1j87NAFlezz2pJ6BkCNAnlKrz1tHo0I793oZ8cfTFVct_0ueqFEz3OtLjiWcLCX90oM1Ux_BZuWr7l6PzvEaImxxrTqIy9M5NMn_RDEG3Gk-wfJHvdOF8XC-u1A==">29</a>.</li>
<li><strong>Alerting:</strong> The size of the DLQ is a critical metric; growing DLQ depth should trigger alerts for manual intervention <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEATF-8ojdZKpkXPPqDJmcexidA8Fo4g8M1737OwRPdyZbSoXi0RKkhctGTp62J5PReRU1dihQLC-Rr9o32-K-Gy2KjqBWlMjZa1hW2qWQtCVPYiNMXB-p1jRIfT9xLvPQL84kZg3YwHlxDck22cDUqRP94Uc7d3W5HuZNc2A87PCmU8peDNqbza0zPKZmTQuFZ9gaJsw==">17</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHdp9hSU08oYMf-4GdNFGBYU4v4d93LvZ1d_ICLNnJcPAG2fUTOKHohQ-kOnJsaA6Teu2FI8Ut30grgFQBpXeaydSjx9bUWXZoU_hSLg_aL73uSS9CsVmK0LFDKAtq3YG4D31h1Kuszk39-Lk3nu09_NajWdBOTQy-_M8QxfRrEU9fL6K4WYNU28bRd_69TmlowBm2krMq_IWLW-qzdtaPgV3zuPDnB1w==">19</a>.</li>
</ul>
<h5 id="33-circuit-breaker-pattern"><strong>3.3 Circuit Breaker Pattern</strong></h5>
<p>While retries handle individual message failures, the Circuit Breaker pattern protects the entire ecosystem from total collapse during prolonged outages <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEii1RdCo-T6eB4tvb4eqOGUFXXz86BAwvINlXVzeqixPAe4JoWDgnCEBSXVxkmYsG2grSzKTsx0HES6tGxKTNLiu4QWsF8F_KirhZWy4T34iyby3QYriXlFhsUBmvP33AQ5xkmqsPGex3VIzZKEhLDJDWx491NGX7SysFplvIamdaIGyhvkTcQnifz1eRmdo5qs1BpvdiRMmCin3gY6-IZcf3KJA==">30</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF4clgaz1Awh_GB47zB8l4w-lRCthsTIWD6SuBJwCTCHaGi9JO0Ap-1rMelQreT6KjtzTWqtapMz6xhm_H3pX7tKtALVd6i8YVM8lV0vuXQuO7ttX5E3ySHcZQPI0sncecV5S_rsxYmhMjxKUkkqxraEdedxVDbgUZvvWCUj0Lcvg==">31</a>.</p>
<p><strong>Functionality:</strong> If a specific endpoint fails a significant percentage of requests (e.g., 100 failures in 60 seconds), the circuit breaker &ldquo;trips&rdquo; to an <strong>Open</strong> state.</p>
<ul>
<li><strong>Open State:</strong> Delivery is paused entirely for that endpoint. The provider stops wasting resources trying to send requests that will likely fail <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF4clgaz1Awh_GB47zB8l4w-lRCthsTIWD6SuBJwCTCHaGi9JO0Ap-1rMelQreT6KjtzTWqtapMz6xhm_H3pX7tKtALVd6i8YVM8lV0vuXQuO7ttX5E3ySHcZQPI0sncecV5S_rsxYmhMjxKUkkqxraEdedxVDbgUZvvWCUj0Lcvg==">31</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF3SixGEwLnJSSDKs0a8OOppywsIW7q2J8aZjaO7L1PLb6bH5CAnbroqLQwsBO0dZZsGDOtgF4aLdZLQeJX-fIw-RqvbES0LRDmTCZyGqvhBk-TVkS3P-XjxaDWdiptgeVjhOTOkUN_0lgqzkZk_G8ObsMcZlqoMpdvykuzrSuRkMSFwJrzsv-Za74qFDK3Y4Ko">32</a>.</li>
<li><strong>Half-Open State:</strong> After a cooldown period, the system allows a limited number of &ldquo;test&rdquo; requests. If these succeed, the circuit closes and normal flow resumes. If they fail, it re-opens <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF4clgaz1Awh_GB47zB8l4w-lRCthsTIWD6SuBJwCTCHaGi9JO0Ap-1rMelQreT6KjtzTWqtapMz6xhm_H3pX7tKtALVd6i8YVM8lV0vuXQuO7ttX5E3ySHcZQPI0sncecV5S_rsxYmhMjxKUkkqxraEdedxVDbgUZvvWCUj0Lcvg==">31</a>.</li>
<li><strong>Distributed State:</strong> In large systems, the state of the circuit breaker is often managed in a distributed store like Etcd or Redis to synchronize awareness across all delivery nodes <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHnX_dTTnINzT7X8e5hlMotKAfsSndIjiQk6jQzsN796QPbtzgqxv0KSq6614IFuC9MAo0fT6uUi2CDRsw6G53zjPoVha2es6TiRmz4pIDeoSqk0fWjovhwbbG5xLVvm5-w1FC_vXwRiop2PfXAg089Czp7PKLpL6MyoDQT5Js3vAPE0VXfSxsBgTC6ytO63A==">33</a>.</li>
</ul>
<h3 id="4-scalability-and-payload-design"><strong>4. Scalability and Payload Design</strong></h3>
<p>As systems grow, the volume and complexity of webhook events increase. Proper design choices in payloads and traffic management are essential for long-term maintainability.</p>
<h4 id="41-handling-high-volume-bursts"><strong>4.1 Handling High-Volume Bursts</strong></h4>
<p>Webhook traffic is rarely uniform; it is &ldquo;bursty&rdquo; by nature (e.g., bulk updates triggering thousands of events) <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFd_CxUJPKeVMz1F02SqlVQjKBwFSscQ8mtFX755DcIRg6EU2j5-mjqOJu8hflle4-VnsqJbFMLbfTmn5fZUWuoq6RBBxRAlRKJWTYK0i9j9b7_ZTFlbVCRcc2DLxpvESUfrCiUPBj_a01pRXCaQ3lSo4v-5yuecvnWx6GAnnWg8M4vN4G1PnrSbZL-XcgBmsFqINYgUuI0b3yQR_BgMITXm7G1BCZ2Mm4=">23</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGlXCpA03cjm2HhSB_1tZBCddlHw7t9zrzGIWVyeXRPhmyshdf1u7gelYqt4L3TVML4ucYYal6EABFM4XOGo8fz22QJzpFqFiSJFwsJsCTx1dCeqo0uBGSUyYFCE8JHAwxUNXmLt6cLY6hv0FYzRnFRFHFpj2_4l1Fv9HLO8X0Mzlc0XAdVF_GB5r9ViToyKWk=">34</a>.</p>
<p><strong>Rate Limiting:</strong> Providers should enforce rate limits on outgoing webhooks to prevent overwhelming consumer endpoints. This smoothens traffic spikes into a consistent stream <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEZ-84ZM44XCpM1vFVUNfrYG0zPZ6SeH4Y4G_nIIuv2XBee_SscnA4KYHIYW0g3CGFzDadJjeR0BpGnOwWpreUXbvbMWdcWDvObDBqUN-mKwKQMV8DHKCYKtBhcz4Ex5nSeg10PQzabmIIMqEfNMxhFt-CAL-1yOA==">35</a>. If the limit is reached, requests are throttled or queued rather than dropped <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEZ-84ZM44XCpM1vFVUNfrYG0zPZ6SeH4Y4G_nIIuv2XBee_SscnA4KYHIYW0g3CGFzDadJjeR0BpGnOwWpreUXbvbMWdcWDvObDBqUN-mKwKQMV8DHKCYKtBhcz4Ex5nSeg10PQzabmIIMqEfNMxhFt-CAL-1yOA==">35</a>.</p>
<p><strong>Buffering:</strong> For self-hosted solutions, an intermediate buffering layer (like NGINX or a lightweight &ldquo;Holding the Load&rdquo; service) can accept connections rapidly and persist requests to storage before they reach the heavier application logic <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFd_CxUJPKeVMz1F02SqlVQjKBwFSscQ8mtFX755DcIRg6EU2j5-mjqOJu8hflle4-VnsqJbFMLbfTmn5fZUWuoq6RBBxRAlRKJWTYK0i9j9b7_ZTFlbVCRcc2DLxpvESUfrCiUPBj_a01pRXCaQ3lSo4v-5yuecvnWx6GAnnWg8M4vN4G1PnrSbZL-XcgBmsFqINYgUuI0b3yQR_BgMITXm7G1BCZ2Mm4=">23</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGi86SD96fW-xFK_X84I9lPylpM6VBZGAFibU34NUtVmx1LcRW_HLP4fOYtJv2_vNff25pqSYwAq09S2tH4W6kCBXrao2NytEeUU0voKlbGnJfgslr823r8QuWJvJj3FPv3qh8i9Xwwx1VcRZbWiwCnjYdbQj-tVOgPkWIvrew8cQShYszht98=">36</a>.</p>
<h4 id="42-payload-design-fat-vs-thin-events"><strong>4.2 Payload Design: Fat vs. Thin Events</strong></h4>
<p>The content of the webhook payload involves a trade-off between efficiency and coupling.</p>
<ul>
<li><strong>Fat Payloads (Event-Carried State Transfer):</strong> The payload contains the full resource state (e.g., the complete Order object).
<ul>
<li><em>Pros:</em> Decouples systems; the consumer doesn&rsquo;t need to call back to the API to get data <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHKAYtR111JaUonVfus71SW3CQhTGuLaDlWY4AUaUzUzmLsqVmLSHvk32ut_5dkYafGZDCGL7ci6IFm4zM4TB160dmZLb7vILSvAOb99uHcarUrBEtL_ixdCugN60yQge0LND250y0z0JZXJMA=">37</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEurOYKLl9vW8_iQij2gRKetKr5a55aofIDB6vAl2O4TRlpJGiu4sBVeyq1l2RglZ0GgTjjoEp9Ig_MPyP5kdg7zbBA2jKLcn4-n-fczZkQw5joT-5kkDiJZfXjZz2KENyZ4XiF2PmJ-Cr99LHk">38</a>.</li>
<li><em>Cons:</em> Can leak sensitive data if not carefully filtered; payloads can become large (bloat); data might be stale by the time it is processed <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHKAYtR111JaUonVfus71SW3CQhTGuLaDlWY4AUaUzUzmLsqVmLSHvk32ut_5dkYafGZDCGL7ci6IFm4zM4TB160dmZLb7vILSvAOb99uHcarUrBEtL_ixdCugN60yQge0LND250y0z0JZXJMA=">37</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHJ0JCD1woL-NaTbyw-j7PlMbtwFt8RV_ZHv7G1aloJ4-ZUHNSRId0zvqSgnaI6Ec0QIhOiRuSz_YqX7cTbeOVZpSKllcWSrmdgnqpvVGtlPdGCVZ2uN5Of">39</a>.</li>
</ul>
</li>
<li><strong>Thin Payloads (Event Notification):</strong> The payload contains minimal data, typically just the event type and resource ID (e.g., {&ldquo;event&rdquo;: &ldquo;order.created&rdquo;, &ldquo;id&rdquo;: &ldquo;123&rdquo;}).
<ul>
<li><em>Pros:</em> Secure (no sensitive data in payload); ensures consumer fetches the latest state <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH7IgdgnjAYn56-UBWDUh9NHS55EnqLdOmTTx2JtV41oh7Wb7TjWnjHmNS-qkW3fAjQyvGFwbmCFyfAon-Lp7X1xJxaZAy9eHfFlEAXO9K3AJQ0U02vMI-YRUUpMK7Eqa5Q">40</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFD9i0zWR3HkaI2O4KoryMXqqgfE4NbOWvFDZM_hz0JjFAJXvi8wbVUDXbusd11PlzqseeNcBpFrj-L827YVMqnifjpnPyWo0lYQ71KbDn-gv-hfbN97e7oQTgg-2QikZzqNvujeXzv-3UsFfqNdeKKKSiM6qSsDQ==">41</a>.</li>
<li><em>Cons:</em> Increases API load due to &ldquo;callback&rdquo; traffic (consumer receiving the event immediately calls the API to get details) <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHKAYtR111JaUonVfus71SW3CQhTGuLaDlWY4AUaUzUzmLsqVmLSHvk32ut_5dkYafGZDCGL7ci6IFm4zM4TB160dmZLb7vILSvAOb99uHcarUrBEtL_ixdCugN60yQge0LND250y0z0JZXJMA=">37</a>.</li>
</ul>
</li>
<li><strong>Best Practice:</strong> Many systems use a hybrid or offer &ldquo;Thin&rdquo; events by default for security, with optional expansion for trusted internal consumers <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHJ0JCD1woL-NaTbyw-j7PlMbtwFt8RV_ZHv7G1aloJ4-ZUHNSRId0zvqSgnaI6Ec0QIhOiRuSz_YqX7cTbeOVZpSKllcWSrmdgnqpvVGtlPdGCVZ2uN5Of">39</a>. Ideally, keep payloads under 20kb to reduce transmission overhead <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEzWmUtLUwZardpkMkEkiRaaZ9kjxC2qM52ESoHE9K-Km6HdJrMGp3qvZPFszYFVZI8nX6mL7rraaFWc-eTJ4C6h27FI1ICYJrHqj9T2_oirMHa1SIi8q9eNdqFro_ZwDtGP-se6PNnK6CGFVgD7HwMR9tmQdo2R43ldM65VhuH8syF6-HpRJwW_QofJGiRS3I=">42</a>.</li>
</ul>
<h4 id="43-schema-validation-and-versioning"><strong>4.3 Schema Validation and Versioning</strong></h4>
<p>Webhook payloads constitute an API contract. Breaking changes (removing fields, changing types) can cause downstream failures.</p>
<p><strong>Versioning:</strong></p>
<ul>
<li><strong>Event-Type Versioning:</strong> Versioning is best applied per event type (e.g., v2.invoice.paid vs invoice.paid) rather than a global API version, as this allows granular evolution <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEjc8rQy3dziZ30c5XTfB6f2g7Ro01RtQ4DneCpqeTVp2RbNtd1GqtlzJSZrkJv-BCVl8ANtZAADBSG8yTKyYdHKwQ619at7JzBhX0EDjsUpIyLdfpGQbsNsJgwPYxVqmwrT-A=">43</a>.</li>
<li><strong>Additive Changes:</strong> Schema evolution should be additive (adding new fields is safe; removing fields is breaking). Deprecation periods are required for removing fields <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH55diJUzcJR_u2AWR3Mr6S8HabBX1seyCwwOzAXTTU96fhqNP4RUhs06bklN1nNYw8eTQQd6K9c9NXUVwJ6kJN4y1CD8PMECf1LHUmz7X04825mo3sNLM72Er8DgB0tZ7SSlZl1-y2knwdSj8lxRwVWilPCBC9sGj3LHy_KBcphb5G6bcrehvsRDvgmioQzQepSjQOWAFn">21</a>.</li>
</ul>
<p><strong>Validation:</strong> Consumers should validate incoming payloads against a JSON Schema to fail fast if the data structure is malformed <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE1TYnifHubdovywYD06VK3FtXJhHcToZGBll41Wz8DPfV4_uDdBMVhtWVxvSkoQ69sS-jXup5s_S9ECebOiDmluUQNqH4zKG7jh_HPBKoLMGVEzOH8nF27HUYZ-NReVd9wEPydG49wiPV1A7hvoBk=">44</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEnqdOCSSodq7lxr2auzJYrSMay7bX5jsonu_qtxORBvk9WCP3zotT4Aqkn5aCcuRu27MCmNGYYV62TFJYCh6DTzFyv7RsloSSo84R4Tl31M-BFtBCMdDPUquMu7t4LuQ==">45</a>. However, validation logic should be permissive (&ldquo;tolerant reader&rdquo; pattern)—ignoring unknown fields to maintain forward compatibility <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH55diJUzcJR_u2AWR3Mr6S8HabBX1seyCwwOzAXTTU96fhqNP4RUhs06bklN1nNYw8eTQQd6K9c9NXUVwJ6kJN4y1CD8PMECf1LHUmz7X04825mo3sNLM72Er8DgB0tZ7SSlZl1-y2knwdSj8lxRwVWilPCBC9sGj3LHy_KBcphb5G6bcrehvsRDvgmioQzQepSjQOWAFn">21</a>.</p>
<p><strong>Data Minimization:</strong> To comply with privacy regulations (GDPR, CCPA), payloads should minimize Personal Identifiable Information (PII). Sensitive data should preferably be retrieved via a secure API call (Thin Payload) rather than broadcasted in the webhook <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH12GnFNNVZoTt8EZ7ZELuQ2jyM2MTTg6fsUKd1Qv0j0xbJyJXE6P5wB89sRaqCHdCdkRPtra8E2C_-lrPXWn96F_s1PpwosEeiwqalYPXcS57HDounBKc5L1EXkPBEtg==">6</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE6mQdSQzonHQsRQLG1x2iQbAc14h1b0QyfDZPE1MsFLTlnRODkc6cJqhFJfn5sTtonCXscipnJQhEOtW1TgQoZt9bJ5wG-o8XJY9hWEw2iHJOXWDuFMxbviQLx5h-_dgoXF-Op">14</a>, <a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFD9i0zWR3HkaI2O4KoryMXqqgfE4NbOWvFDZM_hz0JjFAJXvi8wbVUDXbusd11PlzqseeNcBpFrj-L827YVMqnifjpnPyWo0lYQ71KbDn-gv-hfbN97e7oQTgg-2QikZzqNvujeXzv-3UsFfqNdeKKKSiM6qSsDQ==">41</a>.</p>
<h3 id="conclusion"><strong>Conclusion</strong></h3>
<p>Implementing a robust webhook system requires a holistic approach that balances security, reliability, and efficiency. By securing the transport with HMAC and mTLS, ensuring reliability through idempotency and retries, and designing for scale with asynchronous queues and circuit breakers, developers can build event-driven architectures that are resilient to the chaotic nature of distributed networks. The separation of concerns—where the ingestion layer strictly handles intake and the worker layer handles processing—remains the fundamental architectural pattern for successful high-volume webhook implementations.</p>
<p><strong>Sources:</strong></p>
<ol>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEQCXOGuJl7Dh-VhImsadFbEWJxadTB33mDMPL2zHVmbHLv1IfGzFfSEOPmNZolZ00UFxk13zMvSO9B2llkj2RvC8tHuUF3bSc8c9KRD4Ww0yIH_1EbuKPDj03sOz5Y0VsLzTJPPh1TVzK4n5l8kLgSZmrmFROXpNMvIxvs5rjDIr2lIYRLaEUpgcM6uqk=">ngrok.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGapOwdbfMw2xiKf7LQTypi27arEInXlcwMSM0OvS9Irikt1PHqfjKo7eYX1-GSXVUnL1OZKvJPBglA0qm3gwyUNEteJbfdTzPs3rGU1u9KFaVgnecU31VCHI9sAoLW6216rF5E_DmHOhb7g2DP-SfhA7Nv6OxoS08HZEaK-XGw3LBF5NG7W8n4kww_Iw8j6DXuhqLF3g==">medium.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHtLMqFzEPWWTqr57hgCAajV8f2Ij_c5UJeD5WwDVNpUV3KinmSkEGvoP7IlL0h77x5SOjddKsCURFSbuoHB9N0fMQEuZwA6vaxQ3RQb8C4dnCsp7Y0pcosupydpCz5fIJL0A-J8BadBDgNFkVG_TUiGKVx6nm4T8D5sPbuTg==">loginradius.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFS-EcH1LqHZM6UuNXeP-qitgt8WWv5rnN9tweCAL1n2Ek29RI_lGbpCGS6GSkPw2HsuOwmoxLEfSQB5p03WIGFpYueJRELK05i8aXPElmhgw9Wnxt9geuz9jBrOzWvRNiUC2Jtfh0vM0lO2-uzvqU=">stytch.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEXs9GPnNDFPvttHxuwH1fmRNaG7g0AEVP78LNYuD72y5prRsjxAWF1SJ7WgvtvtIU8dmVKOPBXtRWsieTrZQMM7OXct27gnZKRZAdfgsbQTPepbGNrXnoGvjH2hSqBMbdh04kEeOZmgZ3qyw==">dev.to</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH12GnFNNVZoTt8EZ7ZELuQ2jyM2MTTg6fsUKd1Qv0j0xbJyJXE6P5wB89sRaqCHdCdkRPtra8E2C_-lrPXWn96F_s1PpwosEeiwqalYPXcS57HDounBKc5L1EXkPBEtg==">webflow.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE3k1o-3NRkMcffQX8rtbkeOZtI6fj-vyPodgDETt437-jH_lOLx0vKahj92Vaxo28UE8xXjEAAigXWq6jNmbJysY_GhRMoQY9vw_gREej2hXsZ4atFoO-NPyd9pmC9cQkh3VEvU8MCvS8=">webhooks.fyi</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFXhbKv9jWGpppHz-I9OAOAkGHbvPQGssu-IzoCcTn7-AJvw_KRC0mtAWzp0ML2hS1UkoasxuhHfOCySlYdoFKmGGbOqw2usCXYiQfNz2wzBJ-46HpNCfh_OiX64LsO2iBrv-nH9tDfEZhTYUlPnjQwmolyARu3jSigFIC8KYykE04XjV8ccUWP_CLCz6tVC516k0WI2SfUEoFzX1uQhE6xJa4rDEq6JhURZuEt7rhKDNqURA==">latenode.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEDIwllRPtSE_Pf62YeXgIYgsDEfs-1wHuv9ezOKwFYSaEc39N4e_yAOhbtRX1t-lLnt_VqbdnEsbq1uSII6uRxqit-ZihDjM4StrhiNYPplF_yDLSKzglCmQg_JtAsbsmocQQU6WsYQo0kdLodcetTYvUPbQWjEMuZcovNsV0IvMRm4JFcWykx9BIBvrIMKg999w==">security.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGImss29WCJrZ-7A1qRvtpSXx2770lcn-FKzrohjNSvahorZqWNKElMcdM8ncv0_woeJHwVb1-xgukNmufkutg13vEv6wXuSWlpA5Xez7xFPYWUO9B1_c1B645Bj1Tfjmr8KEQr8A==">webhookrelay.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFYYHzohwba_5v9CJiFs8mD1iz9fbhE8MtwfSvmrUpiSIuWRvl2kvOSLUz9Uvjk6_5n-fLTAnIyoWJe2zh6vCb1i0m2bmHtT2vlK_jU2o-aPA0hlfcOtinI7exwqNXqj9Vrv1Oq6--c0TPo-NGY23eKvhemf-YeZ6UQWTITLFB1oWHphtQzzR2dfuIB4IYT3URzUbF8VVUosF8=">techradar.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHyAw6S_aOVlsVoLFQg0ZMCQlWmrBgVHp4dBnn2xttREglexTIahmODOAstaUnlwsNIP87xlAcCBSasuOixIkwZ6P5b6tzO9rqx4rahyPJ98wCyFn9P_A3mpjCOu7wNm_xYBANICeyhNA==">dev.to</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH1xca3J3HdA9cCG1LqCARxi2f2drvrB3kvAEkgwonEY73ciX1Lfc37H71xRSwdAwybTc7UIK6pLHVBmlgaa5oQG-hkozuAkTN_ESLLLgMFL3uXCe_YKsU4QDCfxWE9V8YUG8_9iVClMHZsmwa0Ny7ZUUTFq_7IPBGSW85ourPVJ9I=">hookdeck.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE6mQdSQzonHQsRQLG1x2iQbAc14h1b0QyfDZPE1MsFLTlnRODkc6cJqhFJfn5sTtonCXscipnJQhEOtW1TgQoZt9bJ5wG-o8XJY9hWEw2iHJOXWDuFMxbviQLx5h-_dgoXF-Op">snyk.io</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEQfVAEpbMLruPbjNGZ738eh_ylZHWHPnu_qZm9XTLHB-AT0BmDjUV_H1Lv00QMDQhFo9DgyxKxPUtoHpGCr0y7XlujSMPRVNqbFaRcmSCOGLV78IAM-ewJFRv_ttzj2JOp_tgD2udxcyAIsAUbcLhsS1uYoNKoJkI=">hookdeck.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEHXKe8AnCoBNUic4_dIw3dZ5D6GIcUTosndmVu1nzaiSfbBhtV04q7oMzc4l71nWdcRfuExdy1qe2cHZj_2E8QGFjwSWWlQthKKPP3YDgs9Wm5ZiXPAS6z7rAhkvllqe6Uw73rz9UGA1IPPZ_-RI4h124D0AocAIjiY798wYZn5_k0U_K1qAcdSG9jxHTwuFcaEEUz9g==">medium.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEATF-8ojdZKpkXPPqDJmcexidA8Fo4g8M1737OwRPdyZbSoXi0RKkhctGTp62J5PReRU1dihQLC-Rr9o32-K-Gy2KjqBWlMjZa1hW2qWQtCVPYiNMXB-p1jRIfT9xLvPQL84kZg3YwHlxDck22cDUqRP94Uc7d3W5HuZNc2A87PCmU8peDNqbza0zPKZmTQuFZ9gaJsw==">medium.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHMukyFGeJn3lzizl34s01hqUqLH39KV4C3rqM6KdUEdBXPX9w1RSuEFAKZT_UX0C1NZu8KL_-4t3UzO0_cg69Nb_JI_u4dk8GMpFAhLqOOqPO8FuD3hgDsb45rLo0v8o1ZlOeLBpF2ZcZ3KXE7CPR-5ErLgSbfb9k8mPvJXOnpxUg1xtJoa0ynC8XFF0c=">medium.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHdp9hSU08oYMf-4GdNFGBYU4v4d93LvZ1d_ICLNnJcPAG2fUTOKHohQ-kOnJsaA6Teu2FI8Ut30grgFQBpXeaydSjx9bUWXZoU_hSLg_aL73uSS9CsVmK0LFDKAtq3YG4D31h1Kuszk39-Lk3nu09_NajWdBOTQy-_M8QxfRrEU9fL6K4WYNU28bRd_69TmlowBm2krMq_IWLW-qzdtaPgV3zuPDnB1w==">hookdeck.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH0JVPUOmptuBYlB8-wJwyjeuTAVRVyUb223KovQt8MsM-mlOtej6D_kcchgaC2ZwcjcEx0wq13bm76jEk23wIs0we5HpQ0QAgfz9A2FMmhmzNDMGbHA_jg0dMemVpSjrN_B-dQh6lKCPSh52kD7ND2Z6JdPrzXn_zdyqD3Tv4NZzOIHCfNK5myi6e-LOOUybc=">dev.to</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH55diJUzcJR_u2AWR3Mr6S8HabBX1seyCwwOzAXTTU96fhqNP4RUhs06bklN1nNYw8eTQQd6K9c9NXUVwJ6kJN4y1CD8PMECf1LHUmz7X04825mo3sNLM72Er8DgB0tZ7SSlZl1-y2knwdSj8lxRwVWilPCBC9sGj3LHy_KBcphb5G6bcrehvsRDvgmioQzQepSjQOWAFn">medium.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQED662YLvMnAViKnlF958nxB2BmJpS8SQlmVbMuE3Ko2pNQr0b3kaD7B_hyk1HUebgY3IcCs7tGCNRnInAdhxH-LtRvP6C-8byHujzYJChaayy4NZ_BaC3c4VkXU2dSYKuJ">youtube.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFd_CxUJPKeVMz1F02SqlVQjKBwFSscQ8mtFX755DcIRg6EU2j5-mjqOJu8hflle4-VnsqJbFMLbfTmn5fZUWuoq6RBBxRAlRKJWTYK0i9j9b7_ZTFlbVCRcc2DLxpvESUfrCiUPBj_a01pRXCaQ3lSo4v-5yuecvnWx6GAnnWg8M4vN4G1PnrSbZL-XcgBmsFqINYgUuI0b3yQR_BgMITXm7G1BCZ2Mm4=">dev.to</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHJ3oedQbVxZag1bGXe54yIdze9Q9z8q0IPuWb3dPI47uWDHpqJz9cHmjP5-hZ6XYoQ8R-jYwG0hwIahcK4RbJwtYnyZElCsPNpqm4y1nzN-ClzqQ8DV2Fn9v69cFHr-NZnLfaKp7gy1fnIyHCorG7A-TaoWdCCR-6B5Y4CS0vP--U69OqbFvNGJnQB8klIbl36rFWVvvGG6A==">medium.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEkjQjNpTsb8JZ5qe59IFdmmAjGw8COOq_1pGnrUe37t1NDdlwblViRj7cSp7gccbNFGm1n5EQ1r6szF-x9YqTl4P5yKbnDbwfON9RSKjv3IwjZeKTSgVWmATqBkax3VdITfVgQKQpQD8GePx0=">hookdeck.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF99_r_zUtU_jYwFqB_uQJoJXExrO3kKFls8vi9mILvSgAUmdwy6FH_pE_OR796JSUEUJ-tYDqUfYDkStQBVIv7gcPbezN4RfgTSTIUFP-rXh24CgzRm3yr4JFMeqO4WX9_i2cUz6_765Rj7HRrVEEO7tWyRA==">svix.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGrBXavT7Taw429hZQnfpdVUgMSqV0qPk3J1Kr1FYj9RI1REkA6gaHCgEcJpIE3pUUB5LOxm1mWgiZMr7bT1JsOLoNm7PO4TYhMMyLwJ0_HD95w9WrxiqJoLJVJJ4weqq7Xfaa1dOHz1gjxeHd8eNPeQRXEHsJFi6DwWKjldZa99ugI7ObRlXaXiAtlVPdAiY2I8L8pual1oHwnmbgajvVoMq1hSwplMf0ue9Y=">latenode.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEW2f40SZCjfELqmNW1j9NyGNsCNsip2VIO51aLp1fUFClxVo6-KLqVo1OfM-Bevq9PGU_Kf_38maQ4Jmrdxqg7x_H5FmQ3dlUczpv5puiR6LdHG7CPYhZ14eWfHt7G8LHnwfjk_CCMF64KmU9bR5uoSNqShdc6ddPcjZW8_eOBw7M=">amazon.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHx0DNAVU9VSUpsBSRtjwACgynyVes1j87NAFlezz2pJ6BkCNAnlKrz1tHo0I793oZ8cfTFVct_0ueqFEz3OtLjiWcLCX90oM1Ux_BZuWr7l6PzvEaImxxrTqIy9M5NMn_RDEG3Gk-wfJHvdOF8XC-u1A==">integrate.io</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEii1RdCo-T6eB4tvb4eqOGUFXXz86BAwvINlXVzeqixPAe4JoWDgnCEBSXVxkmYsG2grSzKTsx0HES6tGxKTNLiu4QWsF8F_KirhZWy4T34iyby3QYriXlFhsUBmvP33AQ5xkmqsPGex3VIzZKEhLDJDWx491NGX7SysFplvIamdaIGyhvkTcQnifz1eRmdo5qs1BpvdiRMmCin3gY6-IZcf3KJA==">medium.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF4clgaz1Awh_GB47zB8l4w-lRCthsTIWD6SuBJwCTCHaGi9JO0Ap-1rMelQreT6KjtzTWqtapMz6xhm_H3pX7tKtALVd6i8YVM8lV0vuXQuO7ttX5E3ySHcZQPI0sncecV5S_rsxYmhMjxKUkkqxraEdedxVDbgUZvvWCUj0Lcvg==">mambu.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF3SixGEwLnJSSDKs0a8OOppywsIW7q2J8aZjaO7L1PLb6bH5CAnbroqLQwsBO0dZZsGDOtgF4aLdZLQeJX-fIw-RqvbES0LRDmTCZyGqvhBk-TVkS3P-XjxaDWdiptgeVjhOTOkUN_0lgqzkZk_G8ObsMcZlqoMpdvykuzrSuRkMSFwJrzsv-Za74qFDK3Y4Ko">stackoverflow.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHnX_dTTnINzT7X8e5hlMotKAfsSndIjiQk6jQzsN796QPbtzgqxv0KSq6614IFuC9MAo0fT6uUi2CDRsw6G53zjPoVha2es6TiRmz4pIDeoSqk0fWjovhwbbG5xLVvm5-w1FC_vXwRiop2PfXAg089Czp7PKLpL6MyoDQT5Js3vAPE0VXfSxsBgTC6ytO63A==">raymondtukpe.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGlXCpA03cjm2HhSB_1tZBCddlHw7t9zrzGIWVyeXRPhmyshdf1u7gelYqt4L3TVML4ucYYal6EABFM4XOGo8fz22QJzpFqFiSJFwsJsCTx1dCeqo0uBGSUyYFCE8JHAwxUNXmLt6cLY6hv0FYzRnFRFHFpj2_4l1Fv9HLO8X0Mzlc0XAdVF_GB5r9ViToyKWk=">hookdeck.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEZ-84ZM44XCpM1vFVUNfrYG0zPZ6SeH4Y4G_nIIuv2XBee_SscnA4KYHIYW0g3CGFzDadJjeR0BpGnOwWpreUXbvbMWdcWDvObDBqUN-mKwKQMV8DHKCYKtBhcz4Ex5nSeg10PQzabmIIMqEfNMxhFt-CAL-1yOA==">trackunit.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGi86SD96fW-xFK_X84I9lPylpM6VBZGAFibU34NUtVmx1LcRW_HLP4fOYtJv2_vNff25pqSYwAq09S2tH4W6kCBXrao2NytEeUU0voKlbGnJfgslr823r8QuWJvJj3FPv3qh8i9Xwwx1VcRZbWiwCnjYdbQj-tVOgPkWIvrew8cQShYszht98=">medium.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHKAYtR111JaUonVfus71SW3CQhTGuLaDlWY4AUaUzUzmLsqVmLSHvk32ut_5dkYafGZDCGL7ci6IFm4zM4TB160dmZLb7vILSvAOb99uHcarUrBEtL_ixdCugN60yQge0LND250y0z0JZXJMA=">codesimple.blog</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEurOYKLl9vW8_iQij2gRKetKr5a55aofIDB6vAl2O4TRlpJGiu4sBVeyq1l2RglZ0GgTjjoEp9Ig_MPyP5kdg7zbBA2jKLcn4-n-fczZkQw5joT-5kkDiJZfXjZz2KENyZ4XiF2PmJ-Cr99LHk">codeopinion.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHJ0JCD1woL-NaTbyw-j7PlMbtwFt8RV_ZHv7G1aloJ4-ZUHNSRId0zvqSgnaI6Ec0QIhOiRuSz_YqX7cTbeOVZpSKllcWSrmdgnqpvVGtlPdGCVZ2uN5Of">brianlovin.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH7IgdgnjAYn56-UBWDUh9NHS55EnqLdOmTTx2JtV41oh7Wb7TjWnjHmNS-qkW3fAjQyvGFwbmCFyfAon-Lp7X1xJxaZAy9eHfFlEAXO9K3AJQ0U02vMI-YRUUpMK7Eqa5Q">hookdeck.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFD9i0zWR3HkaI2O4KoryMXqqgfE4NbOWvFDZM_hz0JjFAJXvi8wbVUDXbusd11PlzqseeNcBpFrj-L827YVMqnifjpnPyWo0lYQ71KbDn-gv-hfbN97e7oQTgg-2QikZzqNvujeXzv-3UsFfqNdeKKKSiM6qSsDQ==">mendix.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEzWmUtLUwZardpkMkEkiRaaZ9kjxC2qM52ESoHE9K-Km6HdJrMGp3qvZPFszYFVZI8nX6mL7rraaFWc-eTJ4C6h27FI1ICYJrHqj9T2_oirMHa1SIi8q9eNdqFro_ZwDtGP-se6PNnK6CGFVgD7HwMR9tmQdo2R43ldM65VhuH8syF6-HpRJwW_QofJGiRS3I=">github.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEjc8rQy3dziZ30c5XTfB6f2g7Ro01RtQ4DneCpqeTVp2RbNtd1GqtlzJSZrkJv-BCVl8ANtZAADBSG8yTKyYdHKwQ619at7JzBhX0EDjsUpIyLdfpGQbsNsJgwPYxVqmwrT-A=">svix.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE1TYnifHubdovywYD06VK3FtXJhHcToZGBll41Wz8DPfV4_uDdBMVhtWVxvSkoQ69sS-jXup5s_S9ECebOiDmluUQNqH4zKG7jh_HPBKoLMGVEzOH8nF27HUYZ-NReVd9wEPydG49wiPV1A7hvoBk=">inventivehq.com</a></li>
<li><a href="https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEnqdOCSSodq7lxr2auzJYrSMay7bX5jsonu_qtxORBvk9WCP3zotT4Aqkn5aCcuRu27MCmNGYYV62TFJYCh6DTzFyv7RsloSSo84R4Tl31M-BFtBCMdDPUquMu7t4LuQ==">zuplo.com</a></li>
</ol>
</details>

<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>How to Integrate Gemini CLI with Intellij Idea Using ACP</title><link>https://glaforge.dev/posts/2026/02/01/how-to-integrate-gemini-cli-with-intellij-idea-using-acp/</link><pubDate>Sun, 01 Feb 2026 18:50:01 +0100</pubDate><guid>https://glaforge.dev/posts/2026/02/01/how-to-integrate-gemini-cli-with-intellij-idea-using-acp/</guid><description>&lt;p>The &lt;strong>Agent Client Protocol (&lt;a href="https://agentclientprotocol.com/get-started/introduction">ACP&lt;/a>)&lt;/strong>
allows you to connect external AI agents directly into IDEs and text editors that support that protocol (like JetBrains&amp;rsquo; IntelliJ IDEA, PyCharm, or WebStorm, as well as &lt;a href="https://zed.dev/">Zed&lt;/a>).
This means you can bring the power of the &lt;strong>Gemini CLI&lt;/strong> directly into your editor, allowing it to interact with your code, run terminal commands,
and use Model Context Protocol (MCP) servers right from the AI Assistant chat window.&lt;/p></description><content:encoded>
<![CDATA[<p>The <strong>Agent Client Protocol (<a href="https://agentclientprotocol.com/get-started/introduction">ACP</a>)</strong>
allows you to connect external AI agents directly into IDEs and text editors that support that protocol (like JetBrains&rsquo; IntelliJ IDEA, PyCharm, or WebStorm, as well as <a href="https://zed.dev/">Zed</a>).
This means you can bring the power of the <strong>Gemini CLI</strong> directly into your editor, allowing it to interact with your code, run terminal commands,
and use Model Context Protocol (MCP) servers right from the AI Assistant chat window.</p>
<p><strong>This guide will walk you through setting up <a href="https://geminicli.com/">Gemini CLI</a> as a custom agent in <a href="https://www.jetbrains.com/idea/">IntelliJ IDEA</a>.</strong></p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>The JetBrains AI Assistant <a href="https://www.jetbrains.com/help/ai-assistant/acp.html#install-agent-from-registry">help pages</a> mention
that it should be possible to install an AI agent from the ACP registry, but this option wasn&rsquo;t available for me, at the time of this writing.
Hence why I decided to investigate and write this tutorial!</p>
      </div>
    </div><h2 id="prerequisites">Prerequisites</h2>
<ul>
<li><strong>IntelliJ IDEA</strong> (or other JetBrains IDEs) version <strong>2025.3</strong> or later.</li>
<li><strong>Node.js</strong> installed (version 20+ recommended), needed by Gemini CLI.</li>
<li><strong>Gemini CLI</strong> installed.</li>
</ul>
<h2 id="step-1-install-gemini-cli">Step 1: Install Gemini CLI</h2>
<p>If you haven&rsquo;t already, install the Gemini CLI globally using npm:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>npm install -g @google/gemini-cli
</span></span></code></pre></div><p>Once installed, verify it works by running:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gemini --version
</span></span></code></pre></div><h2 id="step-2-locate-the-gemini-executable">Step 2: Locate the Gemini Executable</h2>
<p>You need the absolute path to the installed <code>gemini</code> executable for the configuration file.</p>
<p><strong>On macOS / Linux:</strong>
Run the following command in your terminal:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>which gemini
</span></span></code></pre></div><p><em>Example output:</em> <code>/Users/username/.nvm/versions/node/v22.16.0/bin/gemini</code></p>
<p><strong>On Windows:</strong>
Run the following command in Command Prompt or PowerShell:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#007020">where </span>gemini
</span></span></code></pre></div><p><em>Example output:</em> <code>C:\ Program Files\nodejs\gemini.cmd</code> (or similar inside <code>AppData</code>)</p>

    <div class="admonition important">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>
        <span>Important</span>
      </div>
      <div class="admonition-content">
        <p>Copy this path; you will need it for the next step.</p>
      </div>
    </div><h2 id="step-3-configure-the-acp-agent">Step 3: Configure the ACP Agent</h2>
<p>JetBrains IDEs look for a specific JSON configuration file to load external agents.
You need to create or edit this file.</p>
<p><strong>File Location:</strong></p>
<ul>
<li><strong>macOS / Linux:</strong> <code>~/.jetbrains/acp.json</code></li>
<li><strong>Windows:</strong> <code>%USERPROFILE%\.jetbrains\acp.json</code></li>
</ul>
<p><strong>Configuration Content:</strong></p>
<p>Create the file (if it doesn&rsquo;t exist) and add the following JSON content.
Paste the path you found in Step 2 into the <code>&quot;command&quot;</code> field.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;agent_servers&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;Gemini CLI&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;command&#34;</span>: <span style="color:#4070a0">&#34;/path/to/your/gemini&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;args&#34;</span>: [
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#34;--experimental-acp&#34;</span>
</span></span><span style="display:flex;"><span>      ],
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;use_idea_mcp&#34;</span>: <span style="color:#007020;font-weight:bold">true</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;use_custom_mcp&#34;</span>: <span style="color:#007020;font-weight:bold">true</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div>
    <div class="admonition important">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>
        <span>Important</span>
      </div>
      <div class="admonition-content">
        <p>Be sure to set the <code>--experimental-acp</code> flag, as it&rsquo;s still an experimental feature in Gemini CLI.</p>
      </div>
    </div><h3 id="configuration-breakdown">Configuration Breakdown:</h3>
<ul>
<li><strong><code>&quot;Gemini CLI&quot;</code></strong>: This is the display name you will see in the IDE.</li>
<li><strong><code>&quot;command&quot;</code></strong>: The absolute path to the Gemini executable.</li>
<li><strong><code>&quot;args&quot;</code></strong>: We pass <code>--experimental-acp</code> to tell Gemini to start in Agent Communication Protocol mode. You can also pass other <a href="https://geminicli.com/docs/cli/cli-reference/#cli-options">flags</a> supported by Gemini, for example for forcing a particular Gemini model version, etc.</li>
<li><strong><code>&quot;use_idea_mcp&quot;: true</code></strong>: This is crucial. It allows Gemini to access the IDE&rsquo;s built-in Model Context Protocol (MCP) server, giving it context about your open files, project structure, and more.</li>
<li><strong><code>&quot;use_custom_mcp&quot;: true</code></strong>: Allows Gemini to use any other custom MCP servers you might have configured in the IDE.</li>
</ul>
<h2 id="step-4-restart-and-connect">Step 4: Restart and Connect</h2>
<ol>
<li><strong>Restart</strong> your IntelliJ IDEA to load the new configuration.</li>
<li>Open the <strong>AI Assistant</strong> tool window (usually on the right side).</li>
<li>Look for the <strong>Agent Selector</strong>. It might be a dropdown menu at the top of the chat or a &ldquo;More&rdquo; (&hellip;) menu.</li>
<li>Select <strong>&ldquo;Gemini CLI&rdquo;</strong> from the list.</li>
</ol>
<p>In the AI Chat window, you should see something like this:</p>
<p><figure>
  <a href="#img-7f82f3fb73f1306dd3bf7abe27b62415">
    <img src="/img/gemini-cli/acp-intellij-gemini-cli.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-7f82f3fb73f1306dd3bf7abe27b62415">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/acp-intellij-gemini-cli.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Select <code>Gemini CLI</code> from that drop-down menu.
And you&rsquo;re good to go!</p>
<h2 id="usage">Usage</h2>
<p>Once selected, you can chat with Gemini just like the default AI assistant, but with the added capabilities of the CLI!</p>
<ul>
<li><strong>Context Awareness:</strong> It knows about your project files via the IDE&rsquo;s MCP.</li>
<li><strong>Tool Use:</strong> It can perform actions defined in the CLI&rsquo;s toolset.</li>
</ul>
<h2 id="troubleshooting">Troubleshooting</h2>
<ul>
<li><strong>Agent not appearing?</strong> Double-check the path in <code>acp.json</code>. If you use <code>nvm</code> (Node Version Manager), ensure the path points to the specific version currently in use, not a generic alias that might not be available to the IDE&rsquo;s environment.</li>
<li><strong>Permissions:</strong> On macOS/Linux, ensure the file <code>~/.jetbrains/acp.json</code> is readable by your user.</li>
<li><strong>Experimental Flag:</strong> Ensure you didn&rsquo;t forget the <code>--experimental-acp</code> argument; otherwise, the CLI will try to launch in interactive terminal mode and hang.</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A Javelit Frontend for the Deep Research Agent</title><link>https://glaforge.dev/posts/2026/01/30/a-javelit-frontend-for-the-deep-research-agent/</link><pubDate>Fri, 30 Jan 2026 14:00:54 +0100</pubDate><guid>https://glaforge.dev/posts/2026/01/30/a-javelit-frontend-for-the-deep-research-agent/</guid><description>&lt;p>A month ago, I wrote about &lt;a href="https://glaforge.dev/posts/2026/01/03/building-a-research-assistant-with-the-interactions-api-in-java/">building a research assistant&lt;/a> in Java,
using the Gemini Interactions API, and the Deep Research agent.
Today I wanted to revisit this project, but with the goal to make it more user-friendly, with a &lt;a href="https://javelit.io/">Javelit&lt;/a> based frontend.&lt;/p>
&lt;h2 id="the-research-pipeline-from-query-to-infographic">The Research Pipeline: From Query to Infographic&lt;/h2>
&lt;ul>
&lt;li>First, the user enters the subject of the research.&lt;/li>
&lt;li>A button action triggers the &lt;strong>research of possible topics about that subject&lt;/strong> (ie. the different possible facets or angles of the subject), using &lt;strong>Gemini 3 Flash with Google Search&lt;/strong> activated.&lt;/li>
&lt;li>The user selects the facets they&amp;rsquo;re interested in, to restrict the research to only those aspects.&lt;/li>
&lt;li>Then the &lt;strong>Deep Research agent&lt;/strong> kicks in, via the Gemini Interactions API, and spends a few minutes researching the topic.&lt;/li>
&lt;li>Once the final report is ready, &lt;strong>Gemini 3 Pro creates a solid summary&lt;/strong>.&lt;/li>
&lt;li>The summary is used to generate a &lt;strong>sketchnote with Nano Banana Pro&lt;/strong>.&lt;/li>
&lt;/ul>
&lt;h2 id="a-look-at-the-user-interface">A Look at the User Interface&lt;/h2>
&lt;p>Let&amp;rsquo;s say, as a user, I want to research information about the &lt;em>OpenClaw / MoltBot / ClawdBot personal AI assistant&lt;/em> (unless it has again changed its name? &amp;#x1f603;)&lt;/p></description><content:encoded>
<![CDATA[<p>A month ago, I wrote about <a href="https://glaforge.dev/posts/2026/01/03/building-a-research-assistant-with-the-interactions-api-in-java/">building a research assistant</a> in Java,
using the Gemini Interactions API, and the Deep Research agent.
Today I wanted to revisit this project, but with the goal to make it more user-friendly, with a <a href="https://javelit.io/">Javelit</a> based frontend.</p>
<h2 id="the-research-pipeline-from-query-to-infographic">The Research Pipeline: From Query to Infographic</h2>
<ul>
<li>First, the user enters the subject of the research.</li>
<li>A button action triggers the <strong>research of possible topics about that subject</strong> (ie. the different possible facets or angles of the subject), using <strong>Gemini 3 Flash with Google Search</strong> activated.</li>
<li>The user selects the facets they&rsquo;re interested in, to restrict the research to only those aspects.</li>
<li>Then the <strong>Deep Research agent</strong> kicks in, via the Gemini Interactions API, and spends a few minutes researching the topic.</li>
<li>Once the final report is ready, <strong>Gemini 3 Pro creates a solid summary</strong>.</li>
<li>The summary is used to generate a <strong>sketchnote with Nano Banana Pro</strong>.</li>
</ul>
<h2 id="a-look-at-the-user-interface">A Look at the User Interface</h2>
<p>Let&rsquo;s say, as a user, I want to research information about the <em>OpenClaw / MoltBot / ClawdBot personal AI assistant</em> (unless it has again changed its name? &#x1f603;)</p>
<p><figure>
  <a href="#img-b6abd74633a93990d3932c54635418c3">
    <img src="/img/gemini/interactions/dr-jt/agent-1.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-b6abd74633a93990d3932c54635418c3">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/interactions/dr-jt/agent-1.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>With Javelit, the code looks like this:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Java" data-lang="Java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// The main title</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Jt.<span style="color:#4070a0">title</span>(<span style="color:#4070a0">&#34;🔎 Deep Research Agent&#34;</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// A header</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Jt.<span style="color:#4070a0">header</span>(<span style="color:#4070a0">&#34;Subject&#34;</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// The form containing the text area and submit buttons</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>formSubject<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">form</span>().<span style="color:#4070a0">key</span>(<span style="color:#4070a0">&#34;form_subject&#34;</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>subject<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">textArea</span>(<span style="color:#4070a0">&#34;Subject&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">key</span>(<span style="color:#4070a0">&#34;subject&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">placeholder</span>(<span style="color:#4070a0">&#34;Enter the subject you want to research...&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">use</span>(formSubject);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// The buttons (submit and clear) are inside 2 columns on a row</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>columns<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">columns</span>(2).<span style="color:#4070a0">widths</span>(List.<span style="color:#4070a0">of</span>(0.<span style="color:#4070a0">9</span>,<span style="color:#bbb"> </span>0.<span style="color:#4070a0">1</span>)).<span style="color:#4070a0">use</span>(formSubject);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// I clear the state if the user clicks the clear button</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Jt.<span style="color:#4070a0">formSubmitButton</span>(<span style="color:#4070a0">&#34;Clear All&#34;</span>).<span style="color:#4070a0">onClick</span>(b<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Jt.<span style="color:#4070a0">setComponentState</span>(<span style="color:#4070a0">&#34;subject&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Jt.<span style="color:#4070a0">sessionState</span>().<span style="color:#4070a0">remove</span>(<span style="color:#4070a0">&#34;topics&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}).<span style="color:#4070a0">use</span>(columns.<span style="color:#4070a0">col</span>(1));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// We proceed with the next steps</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// if the user clicks the exploration button</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Jt.<span style="color:#4070a0">formSubmitButton</span>(<span style="color:#4070a0">&#34;Explore Topics&#34;</span>).<span style="color:#4070a0">type</span>(<span style="color:#4070a0">&#34;primary&#34;</span>).<span style="color:#4070a0">onClick</span>(b<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Jt.<span style="color:#4070a0">sessionState</span>().<span style="color:#4070a0">remove</span>(<span style="color:#4070a0">&#34;topics&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}).<span style="color:#4070a0">use</span>(columns.<span style="color:#4070a0">col</span>(0));<span style="color:#bbb">
</span></span></span></code></pre></div><h3 id="1-topic-exploration-gemini-3-flash">1. Topic Exploration (Gemini 3 Flash)</h3>
<p>Then, I click on <code>Explore Topics</code> to find the various facets of that story.
Looks like Gemini 3 Flash is thinking, and is actively searching for the most up-to-date information on Google Search:</p>
<p><figure>
  <a href="#img-3e16b09610e5fbf1d4ac74d744cd9440">
    <img src="/img/gemini/interactions/dr-jt/agent-2.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-3e16b09610e5fbf1d4ac74d744cd9440">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/interactions/dr-jt/agent-2.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Indeed, I created a <em>model</em> interaction invoking Gemini 3 directly, and requesting to return a <strong>structured output</strong> (an array of strings),
containing the different facets of the subject:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ModelInteractionParams<span style="color:#bbb"> </span>planParams<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ModelInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-3-flash-preview&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">input</span>(String.<span style="color:#4070a0">format</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                Find a list of topics to research
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                on the following subject:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                %s
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                &#34;&#34;&#34;</span>,<span style="color:#bbb"> </span>state.<span style="color:#4070a0">subject</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">responseFormat</span>(GSchema.<span style="color:#4070a0">fromClass</span>(String<span style="color:#666">[]</span>.<span style="color:#4070a0">class</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">tools</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GoogleSearch())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">store</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Interaction<span style="color:#bbb"> </span>planInteraction<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">create</span>(planParams);<span style="color:#bbb">
</span></span></span></code></pre></div><p>In terms of UI, as the UI elements are rendered synchronously, as the code is being executed, we can define placeholder elements that will receive future components:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// A header</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Jt.<span style="color:#4070a0">header</span>(<span style="color:#4070a0">&#34;Topics&#34;</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// The form containing the text area and submit buttons</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>formTopics<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">form</span>().<span style="color:#4070a0">key</span>(<span style="color:#4070a0">&#34;form_topics&#34;</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// An &#34;empty&#34; container to hold an info bubble and the future topics</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>topicsContainer<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Jt.<span style="color:#4070a0">empty</span>().<span style="color:#4070a0">key</span>(<span style="color:#4070a0">&#34;topics_container&#34;</span>).<span style="color:#4070a0">use</span>(formTopics);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Jt.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Preparing topics...&#34;</span>).<span style="color:#4070a0">icon</span>(<span style="color:#4070a0">&#34;:hourglass:&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">use</span>(topicsContainer);<span style="color:#bbb">
</span></span></span></code></pre></div><p>In the above, the <code>Jt.empty()</code> component receives the <code>Jt.info()</code> bubble.
But once the list of facets is returned by the model interaction, the info bubble will be replaced by a list of checkboxes with the topics to select:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>topicSelectionContainer<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Jt.<span style="color:#4070a0">container</span>().<span style="color:#4070a0">key</span>(<span style="color:#4070a0">&#34;topics&#34;</span>).<span style="color:#4070a0">use</span>(topicsContainer);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>List<span style="color:#666">&lt;</span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>selectedTopics<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>topics.<span style="color:#4070a0">stream</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">filter</span>(topic<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>Jt.<span style="color:#4070a0">checkbox</span>(topic).<span style="color:#4070a0">use</span>(topicSelectionContainer))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">toList</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Jt.<span style="color:#4070a0">formSubmitButton</span>(<span style="color:#4070a0">&#34;Launch Research&#34;</span>).<span style="color:#4070a0">type</span>(<span style="color:#4070a0">&#34;primary&#34;</span>).<span style="color:#4070a0">use</span>(formTopics);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(selectedTopics.<span style="color:#4070a0">isEmpty</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// wait for user to select topics and hit form submit button</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Gemini 3 found a good list of facets. Let me select the ones I&rsquo;m the most interested in:</p>
<p><figure>
  <a href="#img-fe9027fb45139a4e33de1d18a8d9a83d">
    <img src="/img/gemini/interactions/dr-jt/agent-3.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-fe9027fb45139a4e33de1d18a8d9a83d">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/interactions/dr-jt/agent-3.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h3 id="2-the-deep-dive-deep-research-pro">2. The Deep Dive (Deep Research Pro)</h3>
<p>Now when I click the <code>Launch Research</code> button, the <strong>Deep Research</strong> agent is actively working:</p>
<p><figure>
  <a href="#img-025a1297527f3a20379fee742aabbf47">
    <img src="/img/gemini/interactions/dr-jt/agent-4.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-025a1297527f3a20379fee742aabbf47">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/interactions/dr-jt/agent-4.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>In this part of the interface, you&rsquo;ll notice the use of tabs, to separate the full report, the summary, and the infographic.
This is achieved with the <code>Jt.tabs()</code> component.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// A header</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Jt.<span style="color:#4070a0">header</span>(<span style="color:#4070a0">&#34;Report&#34;</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// The 3 tabs</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>tabLabels<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>List.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;Full Report&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Summary&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Infographic&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>tabs<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">tabs</span>(tabLabels).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Each tab has a placeholder</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>reportPlaceholder<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Jt.<span style="color:#4070a0">empty</span>().<span style="color:#4070a0">key</span>(<span style="color:#4070a0">&#34;fullReport&#34;</span>).<span style="color:#4070a0">use</span>(tabs.<span style="color:#4070a0">tab</span>(tabLabels.<span style="color:#4070a0">get</span>(0)));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>summaryPlaceholder<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Jt.<span style="color:#4070a0">empty</span>().<span style="color:#4070a0">key</span>(<span style="color:#4070a0">&#34;summary&#34;</span>).<span style="color:#4070a0">use</span>(tabs.<span style="color:#4070a0">tab</span>(tabLabels.<span style="color:#4070a0">get</span>(1)));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>infographicPlaceholder<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Jt.<span style="color:#4070a0">empty</span>().<span style="color:#4070a0">key</span>(<span style="color:#4070a0">&#34;infographic&#34;</span>).<span style="color:#4070a0">use</span>(tabs.<span style="color:#4070a0">tab</span>(tabLabels.<span style="color:#4070a0">get</span>(2)));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// The placeholders are info bubbles,</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// soon replaced by the report, summary, and infographic</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Jt.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Preparing full report...&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">icon</span>(<span style="color:#4070a0">&#34;:hourglass:&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">use</span>(reportPlaceholder);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Jt.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Preparing summary...&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">icon</span>(<span style="color:#4070a0">&#34;:hourglass:&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">use</span>(summaryPlaceholder);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Jt.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Preparing infographic...&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">icon</span>(<span style="color:#4070a0">&#34;:hourglass:&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">use</span>(infographicPlaceholder);<span style="color:#bbb">
</span></span></span></code></pre></div><p>As the research and thinking progress, <strong>Deep Research</strong> shares its thoughts and current actions with me
(the info bubbles being replaced by the thoughts and actions):</p>
<p><figure>
  <a href="#img-552574693ec1ccf724e6549e18cfe4e0">
    <img src="/img/gemini/interactions/dr-jt/agent-5.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-552574693ec1ccf724e6549e18cfe4e0">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/interactions/dr-jt/agent-5.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>It&rsquo;s important to pause to see how the thoughts are streamed in real time.</p>
<p>Let&rsquo;s have a look at the agent interaction definition:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>AgentInteractionParams<span style="color:#bbb"> </span>researchParams<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>AgentInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">agent</span>(<span style="color:#4070a0">&#34;deep-research-pro-preview-12-2025&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">input</span>(String.<span style="color:#4070a0">format</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Write a concise research report on the following subject:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &lt;subject&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            %s
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &lt;/subject&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            By focusing on the following topics:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &lt;topics&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            %s
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &lt;/topics&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;&#34;&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>subject,<span style="color:#bbb"> </span>topicsList))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">background</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">stream</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">agentConfig</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>DeepResearchAgentConfig(ThinkingSummaries.<span style="color:#4070a0">AUTO</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">store</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>The important bits here are:</p>
<ul>
<li><code>background(true)</code> to state it&rsquo;s a background operation that can take time to complete.</li>
<li><code>stream(true)</code> to state it should be streamed in real time.</li>
<li>And <code>.agentConfig(new DeepResearchAgentConfig(ThinkingSummaries.AUTO))</code> says that thoughts should be sent as they occur.</li>
</ul>
<p>The part taking care of the streaming is the <code>stream()</code> method on the agent interaction:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>client.<span style="color:#4070a0">stream</span>(researchParams).<span style="color:#4070a0">forEach</span>(event<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(event<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">instanceof</span><span style="color:#bbb"> </span>ContentDelta<span style="color:#bbb"> </span>delta)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(delta.<span style="color:#4070a0">delta</span>()<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">instanceof</span><span style="color:#bbb"> </span>ThoughtSummaryDelta<span style="color:#bbb"> </span>thought)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(thought.<span style="color:#4070a0">content</span>()<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">instanceof</span><span style="color:#bbb"> </span>TextContent<span style="color:#bbb"> </span>textContent)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>Jt.<span style="color:#4070a0">markdown</span>(textContent.<span style="color:#4070a0">text</span>()).<span style="color:#4070a0">use</span>(reportPlaceholder);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(delta.<span style="color:#4070a0">delta</span>()<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">instanceof</span><span style="color:#bbb"> </span>TextDelta<span style="color:#bbb"> </span>textPart)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>reportBuilder.<span style="color:#4070a0">append</span>(textPart.<span style="color:#4070a0">text</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>Jt.<span style="color:#4070a0">markdown</span>(reportBuilder.<span style="color:#4070a0">toString</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">use</span>(reportPlaceholder);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>The client is receiving thoughts, but it&rsquo;s also later going to receive the report in the stream.
So as soon as thoughts or pieces of the report arrive, they are reflected in the UI of our research agent.</p>
<p>And after a little while (from one to six minutes or so) the final report is ready:</p>
<p><figure>
  <a href="#img-0d3157b93b094149e28b7566b3bd79d9">
    <img src="/img/gemini/interactions/dr-jt/agent-6.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-0d3157b93b094149e28b7566b3bd79d9">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/interactions/dr-jt/agent-6.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h3 id="3-synthesis-gemini-3-pro">3. Synthesis (Gemini 3 Pro)</h3>
<p>But I can go straight to the <em>TL;DR</em>, because Gemini 3 Pro will have prepared a high-level summary of the report:</p>
<p><figure>
  <a href="#img-5b7a47074b226a06e5cb2310ff1006dd">
    <img src="/img/gemini/interactions/dr-jt/agent-7.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-5b7a47074b226a06e5cb2310ff1006dd">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/interactions/dr-jt/agent-7.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>The summary is prepared by a call to Gemini 3 Pro:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ModelInteractionParams<span style="color:#bbb"> </span>summaryParams<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ModelInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-3-pro-preview&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">input</span>(String.<span style="color:#4070a0">format</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Create a concise summary of the research below.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Go straight with the summary, don&#39;t introduce the summary
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        (don&#39;t write &#34;Here&#39;s a summary...&#34; or equivalent).
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        %s
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>,<span style="color:#bbb"> </span>reportBuilder))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">store</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><h3 id="4-visualizing-with-infographics-gemini-3-pro-image">4. Visualizing with Infographics (Gemini 3 Pro Image)</h3>
<p>Thanks to the talent of &#x1f34c; <strong>Nano Banana Pro</strong>:</p>
<p><figure>
  <a href="#img-bab0713c8fca7141ad895440d131b538">
    <img src="/img/gemini/interactions/dr-jt/agent-8.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-bab0713c8fca7141ad895440d131b538">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/interactions/dr-jt/agent-8.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>For generating the image, we just pass the summary to the model:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>infographicParams<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ModelInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-3-pro-image-preview&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">input</span>(String.<span style="color:#4070a0">format</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Create a hand-drawn and hand-written sketchnote
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        style summary infographic, with a pure white background,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        use fluorescent highlighters for the key points,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        about the following information:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        %s
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>,<span style="color:#bbb"> </span>summaryText))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseModalities</span>(Interaction.<span style="color:#4070a0">Modality</span>.<span style="color:#4070a0">IMAGE</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="whipping-up-the-ui-with-javelit">Whipping up the UI with Javelit</h2>
<p>What makes this research frontend interesting isn&rsquo;t just the AI logic with the <strong>Gemini Interactions API</strong>,
it&rsquo;s how quickly you&rsquo;re able to whip up a functional web UI for it using <strong>Javelit</strong>,
without the hassle of a complicated web framework.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Info</span>
      </div>
      <div class="admonition-content">
        <p>I invite you to check out my other <a href="https://glaforge.dev/tags/javelit/">articles on Javelit</a> to learn more.</p>
      </div>
    </div><h3 id="the-rendering-loop-philosophy">The Rendering Loop Philosophy</h3>
<p>The core of Javelit is its <strong>rendering loop</strong>.
You provide a lambda that describes your UI (or a <code>main</code> method when running with the <code>javelit</code> command).
Every time an interaction occurs (a button click, a checkbox toggle&hellip;) it re-executes the UI code from top to bottom.</p>
<p>As <a href="https://bsky.app/profile/cyrilou242.bsky.social">Cyril de Catheu</a>
(the creator of Javelit) would put it, it&rsquo;s a bit like a video game loop.
A state update triggers a repaint.
But contrary to video games, catching input and state updates is done automatically by the Javelit components, and
the repaint isn&rsquo;t flying at 60fps, but only when there&rsquo;s a UI interaction.
Because the code runs top-to-bottom, you don&rsquo;t need to manage complex event listeners or manual DOM updates.</p>
<h2 id="conclusion">Conclusion</h2>
<p>By combining the power of specialized Gemini models and agents with the <strong>Gemini Interactions API</strong>
<em>(and my <a href="https://github.com/glaforge/gemini-interactions-api-sdk">Java SDK</a> for it)</em>
with the rapid UI development of <a href="https://javelit.io/">Javelit</a>,
I was able to build a sophisticated research tool,
taking advantage of the powerful <a href="https://ai.google.dev/gemini-api/docs/deep-research">Deep Research agent</a>,
in a fraction of the time it would take with a traditional frontend stack.</p>
<p>The ability to stream thoughts from the <a href="https://ai.google.dev/gemini-api/docs/deep-research">Deep Research agent</a>
directly into a reactive Javelit container makes the whole experience feel &ldquo;alive&rdquo; and transparent.</p>
<p>And <strong>everything in Java</strong>&hellip; No Python was harmed in this exercise! &#x1f603;</p>

    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Info</span>
      </div>
      <div class="admonition-content">
        <p><a href="https://github.com/glaforge/gemini-interactions-api-sdk/blob/main/src/test/java/io/github/glaforge/gemini/interactions/ResearchFrontend.java">Full source code</a>
of this deep research frontend on GitHub.</p>
      </div>
    </div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Executable Markdown Files with gcli-mdrun &amp; Gemini CLI</title><link>https://glaforge.dev/posts/2026/01/26/executable-markdown-files-with-gcli-mdrun-and-gemini-cli/</link><pubDate>Mon, 26 Jan 2026 20:13:39 +0100</pubDate><guid>https://glaforge.dev/posts/2026/01/26/executable-markdown-files-with-gcli-mdrun-and-gemini-cli/</guid><description>&lt;p>Have you ever wanted to turn your cool LLM prompts &amp;amp; tools, research notes, automation ideas, or even a simple &amp;ldquo;todo&amp;rdquo; list into an executable script?
Inspired by a &lt;a href="https://news.ycombinator.com/item?id=46549444">HackerNews post&lt;/a> about &lt;strong>executable Markdown&lt;/strong>,
I&amp;rsquo;m happy to share &lt;strong>&lt;code>gcli-mdrun&lt;/code>&lt;/strong>, a smart little script that allows you to transform standard Markdown files
into executable scripts powered by &lt;a href="https://geminicli.com/">Gemini CLI&lt;/a>.&lt;/p>
&lt;p>This project allows you to create AI-driven automation, pipelines, and autonomous &lt;em>bots&lt;/em> using mere Markdown text files.
You can find the project on GitHub at &lt;a href="https://github.com/glaforge/gcli-mdrun">https://github.com/glaforge/gcli-mdrun&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>Have you ever wanted to turn your cool LLM prompts &amp; tools, research notes, automation ideas, or even a simple &ldquo;todo&rdquo; list into an executable script?
Inspired by a <a href="https://news.ycombinator.com/item?id=46549444">HackerNews post</a> about <strong>executable Markdown</strong>,
I&rsquo;m happy to share <strong><code>gcli-mdrun</code></strong>, a smart little script that allows you to transform standard Markdown files
into executable scripts powered by <a href="https://geminicli.com/">Gemini CLI</a>.</p>
<p>This project allows you to create AI-driven automation, pipelines, and autonomous <em>bots</em> using mere Markdown text files.
You can find the project on GitHub at <a href="https://github.com/glaforge/gcli-mdrun">https://github.com/glaforge/gcli-mdrun</a>.</p>
<h2 id="quick-start">Quick Start</h2>
<p>Imagine a file named <code>weather.md</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span><span style="color:#000080;font-weight:bold">#!/usr/bin/env gemini-run
</span></span></span><span style="display:flex;"><span><span style="color:#000080;font-weight:bold"></span>Use only the Google Search tool to find the answer to the question below:
</span></span><span style="display:flex;"><span>What is the weather like in Paris right now?
</span></span></code></pre></div><p>Run it like any other script:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>chmod +x weather.md
</span></span><span style="display:flex;"><span>./weather.md
</span></span></code></pre></div><p>And you&rsquo;d get something like:</p>
<pre tabindex="0"><code>I will search for the current weather in Paris.
The current weather in Paris is cloudy with a temperature of 7 °C.
Humidity is at 84%. Wind is blowing from the Southeast at 11 km/h.
There is currently no precipitation.
</code></pre><p>The prompt (below the <em>shebang</em> line) is actually executed by <a href="https://geminicli.com/">Gemini CLI</a>.</p>
<h2 id="how-to-install">How to Install</h2>
<p>To get started, you need the <code>gemini-run</code> wrapper script from <a href="https://github.com/glaforge/gcli-mdrun">the <code>gcli-mdrun</code> epository</a>.</p>
<ol>
<li><strong>Download the script</strong>: You can find it in the <code>scripts/</code> directory of the repo.</li>
<li><strong>Install it</strong>: Make it executable and move it to your system path.</li>
</ol>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>chmod +x gemini-run
</span></span><span style="display:flex;"><span>sudo mv gemini-run /usr/local/bin/
</span></span></code></pre></div>
            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>Of course, you&rsquo;ll have to have <a href="https://geminicli.com/docs/get-started/installation/">Gemini CLI installed</a>,
and a valid <a href="https://ai.google.dev/gemini-api/docs/api-key">Gemini API key</a> configured as a <code>GEMINI_API_KEY</code> environment variable.</p>
      </div>
    </div><h2 id="usage--features">Usage &amp; Features</h2>
<h3 id="yolo-mode-autonomous-execution-aka-live-dangerously">YOLO Mode (Autonomous Execution, aka Live Dangerously)</h3>
<p>By using the Gemini CLI <code>--yolo</code> flag in the <em>shebang</em> of your markdown scripts,
Gemini will execute tools and commands automatically without asking for confirmation.</p>

    <div class="admonition danger">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>
        <span>WARNING!</span>
      </div>
      <div class="admonition-content">
        <p>&#x26a0;&#xfe0f; <strong>Use with caution!</strong> &#x26a0;&#xfe0f;</p>
      </div>
    </div><div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span><span style="color:#000080;font-weight:bold">#!/usr/bin/env -S gemini-run --yolo
</span></span></span><span style="display:flex;"><span><span style="color:#000080;font-weight:bold"></span>List all files in the current directory and rename any file
</span></span><span style="display:flex;"><span>with a &#39;.txt&#39; extension to have a &#39;.bak&#39; extension instead.
</span></span></code></pre></div><h3 id="piping-and-pipelines">Piping and Pipelines</h3>
<p>Because <code>gemini-run</code> supports <em>stdin</em>, you can chain multiple markdown scripts together or mix them with standard Unix tools.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cat customers.log | ./step1_extract.md | ./step2_analyze.md
</span></span></code></pre></div><p>And also redirect their outputs to files, with <code>&gt;</code>.</p>

    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>Those familiar with Gemini CLI <a href="https://geminicli.com/docs/cli/custom-commands/">custom commands</a> might find custom commands more useful
in particular for handling inputs or arguments, rather than piping script outputs.</p>
      </div>
    </div><h2 id="real-world-examples">Real-World Examples</h2>
<p>Here are some cool things you can do with <code>gcli-mdrun</code> (look at those 3 <a href="https://github.com/glaforge/gcli-mdrun/tree/main/examples">examples</a> from the repo:</p>
<h3 id="1-automated-release-notes-git-log-summarymd">1. Automated Release Notes (<code>git-log-summary.md</code>)</h3>
<p>This script analyzes your recent git commits (in the git project in the current folder) a
nd generates structured release notes.
It uses the <code>run_shell_command</code> tool to fetch git logs and diffs (using the <code>git</code> command).</p>
<p>I won&rsquo;t copy the whole script here as it&rsquo;s a bit too long, but I&rsquo;d like just to show you the <em>shebang</em> line:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007020">#!/usr/bin/env -S gemini-run --model gemini-2.5-flash --allowed-tools=run_shell_command(git)
</span></span></span></code></pre></div><p>Gemini CLI allows you to specify which tools to allow or forbid, which MCP servers to use or restrict, etc.
Here, I only allowed the execution of the <code>git</code> command via Gemini CLI&rsquo;s <code>run_shell_command</code> tool.</p>

    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>Be sure to check the documentation of Gemini CLI, and its flags, and ideally avoid giving too many permissions to your executable scripts.</p>
      </div>
    </div><p>For example, I applied the script to my <a href="https://github.com/glaforge/gcli-mdrun">gcli-mdrun</a> repository and got this
(after piping the output to the <a href="https://github.com/charmbracelet/glow">glow</a> Markdown highlighter):</p>
<p><figure>
  <a href="#img-b6e391c6502ce42176651020fab9a139">
    <img src="/img/gemini-cli/gcli-mdrun/release-notes.png"
      alt="Release notes for gcli-mdrun"
       />
  </a>
  <figcaption>Release notes for gcli-mdrun</figcaption>
</figure>
<div class="lightbox" id="img-b6e391c6502ce42176651020fab9a139">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/gcli-mdrun/release-notes.png"
    alt="Release notes for gcli-mdrun"
     />
  <div class="lightbox-caption">Release notes for gcli-mdrun</div>
</div>
</p>
<h3 id="2-intelligent-search-google-searchmd">2. Intelligent Search (<code>google-search.md</code>)</h3>
<p>Leverage the power of Google Search directly from your Markdown scripts.
This example fetches real-time information from the web, requesting the weather forecast for Paris:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span><span style="color:#000080;font-weight:bold">#!/usr/bin/env -S gemini-run --allowed-tools=google_web_search,web_fetch
</span></span></span><span style="display:flex;"><span><span style="color:#000080;font-weight:bold"></span>
</span></span><span style="display:flex;"><span>Use Google Search to find the answer to the question below.
</span></span><span style="display:flex;"><span>Don&#39;t use any other tools.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>What is the weather currently Paris?
</span></span><span style="display:flex;"><span>(be sure to use international units exclusively)
</span></span></code></pre></div>
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>Notice how the tools are restricted with an <em>allow-list</em>.</p>
      </div>
    </div><h3 id="3-visual-summaries-with-nano-banana-nano-bananamd">3. Visual Summaries with Nano Banana (<code>nano-banana.md</code>)</h3>
<p>This is where it gets really creative. You can use the <code>nanobanana</code> MCP server
or <a href="https://github.com/gemini-cli-extensions/nanobanana">Nano Banana Gemini CLI extension</a> to generate infographics and mindmaps from articles or search results.</p>
<p>For example, I ran it against one of my recent articles:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span><span style="color:#000080;font-weight:bold">#!/usr/bin/env -S gemini-run --allowed-mcp-server-names=nanobanana
</span></span></span><span style="display:flex;"><span><span style="color:#000080;font-weight:bold"></span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">*</span> Find the key points of the article at
</span></span><span style="display:flex;"><span>  https://glaforge.dev/posts/2026/01/03/building-a-research-assistant-with-the-interactions-api-in-java/
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">*</span> Make a sketchnote mindmap of the article, with pure white background.
</span></span><span style="display:flex;"><span>  Use highlighters to stress important keywords, and colored thick arrows for each section.
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">*</span> Display this infographic
</span></span></code></pre></div><p>And it generated this beautiful sketchnote mindmap:</p>
<p><figure>
  <a href="#img-49215dbbc3132926c209dfb2ad3a7f72">
    <img src="/img/gemini-cli/gcli-mdrun/mindmap.png"
      alt="Sketchnote mindmap of building a research assistant"
       />
  </a>
  <figcaption>Sketchnote mindmap of building a research assistant</figcaption>
</figure>
<div class="lightbox" id="img-49215dbbc3132926c209dfb2ad3a7f72">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/gcli-mdrun/mindmap.png"
    alt="Sketchnote mindmap of building a research assistant"
     />
  <div class="lightbox-caption">Sketchnote mindmap of building a research assistant</div>
</div>
</p>
<h3 id="4-chaining-search-and-graphics-nano-banana-chainmd">4. Chaining Search and Graphics (<code>nano-banana-chain.md</code>)</h3>
<p>You can even pipe the output of a search script into a graphics script.
For instance, getting the weather in Paris from the Google Search script we&rsquo;ve already seen,
and immediately generating a kawaii-style infographic of it by piping the weather output to a &#x1f34c; Nano Banana script&hellip;</p>
<p>&#x2601;&#xfe0f; And then you can get a super-cute output like this one to illustrate the weather! &#x2600;&#xfe0f;</p>
<p><figure>
  <a href="#img-5485ee269996005f7aefaf9fc0e54440">
    <img src="/img/gemini-cli/gcli-mdrun/weather.png"
      alt="Infographic of current weather in Paris"
       />
  </a>
  <figcaption>Infographic of current weather in Paris</figcaption>
</figure>
<div class="lightbox" id="img-5485ee269996005f7aefaf9fc0e54440">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/gcli-mdrun/weather.png"
    alt="Infographic of current weather in Paris"
     />
  <div class="lightbox-caption">Infographic of current weather in Paris</div>
</div>
</p>
<h2 id="conclusion">Conclusion</h2>
<p><code>gcli-mdrun</code> is all about making AI more accessible and integrable into your existing workflows.
By treating Markdown as code, we can bridge the gap between human-readable documentation and machine-executable tasks,
thanks to <a href="https://geminicli.com/">Gemini CLI</a> and a little bit of glue shell script.</p>
<p>Check out the project on <a href="https://github.com/glaforge/gcli-mdrun">GitHub</a>
and tell me what cool and handy executable Markdown scripts you&rsquo;ll create!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Implementing an arXiv MCP Server with Quarkus in Java</title><link>https://glaforge.dev/posts/2026/01/18/implementing-an-arxiv-mcp-server-with-quarkus-in-java/</link><pubDate>Sun, 18 Jan 2026 06:12:06 +0100</pubDate><guid>https://glaforge.dev/posts/2026/01/18/implementing-an-arxiv-mcp-server-with-quarkus-in-java/</guid><description>&lt;p>For my recent presentation at &lt;a href="https://snowcamp.io/">SnowCamp&lt;/a> on
&lt;a href="https://glaforge.dev/talks/2026/01/16/on-ai-standards-and-protocols-focus-on-mcp-and-a2a/">AI Standards &amp;amp; Protocols for AI Agents&lt;/a>,
I decided to &lt;strong>build an MCP server&lt;/strong> to access the &lt;a href="http://arxiv.org/">arXiv&lt;/a> research paper website
where pre-print versions are published and shared with the community.&lt;/p>
&lt;p>My goal was to shed light on some lesser-known aspects of the Model Context Protocol:&lt;/p>
&lt;ul>
&lt;li>&amp;#x1f6e0;&amp;#xfe0f; While the majority of MCP servers use the &lt;strong>tools&lt;/strong> feature to expose actions that LLMs can request to call,&lt;/li>
&lt;li>&amp;#x1f4c4; An MCP server can also share &lt;strong>resources&lt;/strong> (and resource templates), exposing various static assets the AI app might be interested in,&lt;/li>
&lt;li>&amp;#x270f;&amp;#xfe0f; And &lt;strong>prompts&lt;/strong> (and prompt templates) that users can access and reuse to utilize the MCP server effectively.&lt;/li>
&lt;/ul>
&lt;link rel="stylesheet" href="https://glaforge.dev/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&amp;#43;AD&amp;#43;9EeYXUTm&amp;#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
&lt;div class="admonition info">
&lt;div class="admonition-header">&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">&lt;path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/>&lt;/svg>
&lt;span>Info&lt;/span>
&lt;/div>
&lt;div class="admonition-content">
&lt;p>For the impatient, feel free to go straight to the
&lt;a href="https://github.com/glaforge/arxiv-mcp-server">GitHub repository&lt;/a> for the full source code.
The &lt;a href="https://github.com/glaforge/arxiv-mcp-server/blob/main/README.md">README.md&lt;/a> file
gives instructions on how to build, run, configure, and use the server.&lt;/p></description><content:encoded>
<![CDATA[<p>For my recent presentation at <a href="https://snowcamp.io/">SnowCamp</a> on
<a href="https://glaforge.dev/talks/2026/01/16/on-ai-standards-and-protocols-focus-on-mcp-and-a2a/">AI Standards &amp; Protocols for AI Agents</a>,
I decided to <strong>build an MCP server</strong> to access the <a href="http://arxiv.org/">arXiv</a> research paper website
where pre-print versions are published and shared with the community.</p>
<p>My goal was to shed light on some lesser-known aspects of the Model Context Protocol:</p>
<ul>
<li>&#x1f6e0;&#xfe0f; While the majority of MCP servers use the <strong>tools</strong> feature to expose actions that LLMs can request to call,</li>
<li>&#x1f4c4; An MCP server can also share <strong>resources</strong> (and resource templates), exposing various static assets the AI app might be interested in,</li>
<li>&#x270f;&#xfe0f; And <strong>prompts</strong> (and prompt templates) that users can access and reuse to utilize the MCP server effectively.</li>
</ul>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Info</span>
      </div>
      <div class="admonition-content">
        <p>For the impatient, feel free to go straight to the
<a href="https://github.com/glaforge/arxiv-mcp-server">GitHub repository</a> for the full source code.
The <a href="https://github.com/glaforge/arxiv-mcp-server/blob/main/README.md">README.md</a> file
gives instructions on how to build, run, configure, and use the server.</p>
      </div>
    </div><h2 id="the-combo-antigravity--quarkus--java">The Combo: Antigravity + Quarkus + Java</h2>
<p>To implement this server, I selected the <a href="https://quarkus.io/">Quarkus</a> framework (in Java)
and its <a href="https://quarkus.io/extensions/?search-regex=mcp">extensive MCP support</a>
(documented <a href="https://docs.quarkiverse.io/quarkus-mcp-server/dev/index.html">here</a>).</p>
<p>I enlisted <a href="https://antigravity.google/">Antigravity</a> to help me in this adventure.
I pointed my agentic IDE to the <a href="https://info.arxiv.org/help/api/user-manual.html">arXiv API User&rsquo;s Manual</a>
to draft and scaffold my project, and iteratively collaborated with it to expand the coverage of the arXiv API.
It was a pretty productive session! <strong>I highly recommend checking out Antigravity</strong>!</p>
<h2 id="a-look-at-the-arxiv-api">A Look at the arXiv API</h2>
<p>PDFs are accessible at a URL of the form <code>https://arxiv.org/pdf/{paperID}</code>.</p>
<p>The <a href="https://info.arxiv.org/help/api/user-manual.html">arXiv API</a> offers programmatic access to e-prints via HTTP requests
with parameters for searches, specific paper IDs, pagination, and sorting.
Users can build complex queries using boolean operators, phrases, and grouping.</p>
<p>So for searching, I&rsquo;ve created a REST client that I used in my MCP server implementation:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@RegisterRestClient</span>(baseUri<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;https://export.arxiv.org/api&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#555;font-weight:bold">@RegisterProvider</span>(ArxivResponseFilter.<span style="color:#4070a0">class</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">interface</span> <span style="color:#0e84b5;font-weight:bold">ArxivClient</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@GET</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Path</span>(<span style="color:#4070a0">&#34;/query&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Produces</span>(<span style="color:#4070a0">&#34;*/*&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span><span style="color:#06287e">search</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@QueryParam</span>(<span style="color:#4070a0">&#34;search_query&#34;</span>)<span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>searchQuery,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@QueryParam</span>(<span style="color:#4070a0">&#34;id_list&#34;</span>)<span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>idList,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@QueryParam</span>(<span style="color:#4070a0">&#34;start&#34;</span>)<span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>start,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@QueryParam</span>(<span style="color:#4070a0">&#34;max_results&#34;</span>)<span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>maxResults,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@QueryParam</span>(<span style="color:#4070a0">&#34;sortBy&#34;</span>)<span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>sortBy,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@QueryParam</span>(<span style="color:#4070a0">&#34;sortOrder&#34;</span>)<span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>sortOrder);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Unusually, the arXiv API returns results in <strong>Atom 1.0 XML format</strong> (rather than the typical JSON),
providing detailed metadata for both the query and individual articles, including titles, abstracts, authors, categories, and links.</p>
<p>In order to parse the Atom format and map the feeds to Java classes, I simply went with Jackson&rsquo;s XML parser
(perhaps I could have used the venerable <a href="https://github.com/rometools/rome">Rome</a> project).</p>
<p>Here&rsquo;s one of the entities used in the ATOM domain model:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@JsonIgnoreProperties</span>(ignoreUnknown<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Entry</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@JacksonXmlProperty</span>(namespace<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;http://www.w3.org/2005/Atom&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>id;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@JacksonXmlProperty</span>(namespace<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;http://www.w3.org/2005/Atom&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>published;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@JacksonXmlProperty</span>(namespace<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;http://www.w3.org/2005/Atom&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>title;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@JacksonXmlProperty</span>(namespace<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;http://www.w3.org/2005/Atom&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>summary;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@JacksonXmlElementWrapper</span>(useWrapping<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">false</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@JacksonXmlProperty</span>(localName<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;author&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span>namespace<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;http://www.w3.org/2005/Atom&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>List<span style="color:#666">&lt;</span>Author<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>authors;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p><em>(It is somewhat ironic to use the <code>@JsonIgnoreProperties</code> annotation when parsing XML!)</em></p>
<h2 id="lets-start-with-tools">Let&rsquo;s Start with Tools</h2>
<p>What I like about the Quarkus MCP support is that to turn a Quarkus app into an MCP server,
you just need a few Java annotations, and everything is handled for you!</p>
<p>In my <code>ArxivMcpServer</code>, I injected my <code>ArxivClient</code> REST client:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">ArxivMcpServer</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Inject</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@RestClient</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>ArxivClient<span style="color:#bbb"> </span>arxivClient;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Then to define a tool, I used the <code>@Tool</code> annotation:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Java" data-lang="Java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Tool</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Search for papers on arXiv&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>name<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;search_papers&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>Feed<span style="color:#bbb"> </span><span style="color:#06287e">searchPapers</span>(String<span style="color:#bbb"> </span>query,<span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>maxResults,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                         </span>SortBy<span style="color:#bbb"> </span>sortBy,<span style="color:#bbb"> </span>SortOrder<span style="color:#bbb"> </span>sortOrder)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>performSearch(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>query,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>,<span style="color:#bbb"> </span>0,<span style="color:#bbb"> </span>maxResults<span style="color:#bbb"> </span><span style="color:#666">==</span><span style="color:#bbb"> </span>0<span style="color:#bbb"> </span><span style="color:#666">?</span><span style="color:#bbb"> </span>5<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>maxResults,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>sortBy<span style="color:#bbb"> </span><span style="color:#666">==</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span><span style="color:#666">?</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>sortBy.<span style="color:#4070a0">name</span>(),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>sortOrder<span style="color:#bbb"> </span><span style="color:#666">==</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span><span style="color:#666">?</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>sortOrder.<span style="color:#4070a0">name</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>I could have added <code>@ToolArg</code> annotations on the parameters of this method to give the LLM more context on their role, but the parameter names were self-explanatory.</p>
<h2 id="expose-the-taxonomy-as-a-resource">Expose the Taxonomy as a Resource</h2>
<p>For well-known static assets like the taxonomy of all the domain categories of research papers,
you can expose an <strong>MCP Resource</strong>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Resource</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>uri<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;arxiv://taxonomy&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;List of arXiv categories and their codes&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>mimeType<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;text/markdown&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>TextResourceContents<span style="color:#bbb"> </span><span style="color:#06287e">getTaxonomy</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>TextResourceContents.<span style="color:#4070a0">create</span>(<span style="color:#4070a0">&#34;arxiv://taxonomy&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        # arXiv Category Taxonomy
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        ## Computer Science (cs)
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        *   **Artificial Intelligence** (cs.AI)
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        *   **Computation and Language** (cs.CL)
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        *   **Computer Vision** (cs.CV)
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        *   **Machine Learning** (cs.LG)
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        *   **Robotics** (cs.RO)
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        *   **Software Engineering** (cs.SE)
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        *   ... and many more.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        ## Physics
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        ...
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Again, an annotation, <code>@Resource</code>, is all that&rsquo;s needed to define a resource.
We give it a URI, a description, and a MIME type.</p>
<p>Resources are either text content or binary content.
So depending on the type of your resource,
your methods can return either <code>TextResourceContents</code> or <code>BlobResourceContents</code>.
Here, for the taxonomy, it&rsquo;s just Markdown text.</p>
<p>Since there is only one known taxonomy, the resource name is static and explicit.
However, you can also take advantage of <strong>MCP Resource Templates</strong> which support parameterization.</p>
<p>This is the case, for example, when accessing metadata for each paper.
So I created a resource template as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@ResourceTemplate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>uriTemplate<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;arxiv://papers/{id}/metadata&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;The full metadata of the arXiv paper&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>mimeType<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;application/json&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>TextResourceContents<span style="color:#bbb"> </span><span style="color:#06287e">getMetadata</span>(<span style="color:#555;font-weight:bold">@ResourceTemplateArg</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>id)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Feed<span style="color:#bbb"> </span>feed<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>performSearch(<span style="color:#007020;font-weight:bold">null</span>,<span style="color:#bbb"> </span>id,<span style="color:#bbb"> </span>0,<span style="color:#bbb"> </span>1,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(feed.<span style="color:#4070a0">entries</span><span style="color:#bbb"> </span><span style="color:#666">!=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span><span style="color:#666">&amp;&amp;</span><span style="color:#bbb"> </span><span style="color:#666">!</span>feed.<span style="color:#4070a0">entries</span>.<span style="color:#4070a0">isEmpty</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>TextResourceContents.<span style="color:#4070a0">create</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#4070a0">&#34;arxiv://papers/&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>id<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;/metadata&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>jsonMapper.<span style="color:#4070a0">writeValueAsString</span>(feed.<span style="color:#4070a0">entries</span>.<span style="color:#4070a0">get</span>(0)));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">catch</span><span style="color:#bbb"> </span>(JsonProcessingException<span style="color:#bbb"> </span>e)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">throw</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>RuntimeException(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#4070a0">&#34;Failed to serialize paper metadata&#34;</span>,<span style="color:#bbb"> </span>e);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">throw</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>RuntimeException(<span style="color:#4070a0">&#34;Paper not found: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>id);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>I used an <code>@ResourceTemplate</code> annotation, and the <code>uri</code> parameter is replaced with <code>uriTemplate</code> which contains a placeholder for the ID of the paper.
That paper ID is actually passed as a parameter to the method, and this parameter is annotated with a <code>@ResourceTemplateArg</code> annotation.</p>
<p>Again for templates, it&rsquo;s like for plain resources, you either return <code>TextResourceContents</code> or <code>BlobResourceContents</code> for binary content.</p>
<h2 id="prepare-reusable-prompts-for-the-user">Prepare Reusable Prompts for the User</h2>
<p><strong>MCP Prompts</strong> are prompts for the user to use to make the best possible use of the MCP server.</p>
<p>Here&rsquo;s a method returning a prompt to get summaries of papers:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Prompt</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>name<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;summarize_paper&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Summarize the given paper&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>PromptMessage<span style="color:#bbb"> </span><span style="color:#06287e">summarizePaper</span>(String<span style="color:#bbb"> </span>id)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Feed<span style="color:#bbb"> </span>feed<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>performSearch(<span style="color:#007020;font-weight:bold">null</span>,<span style="color:#bbb"> </span>id,<span style="color:#bbb"> </span>0,<span style="color:#bbb"> </span>1,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(feed.<span style="color:#4070a0">entries</span><span style="color:#bbb"> </span><span style="color:#666">!=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span><span style="color:#666">&amp;&amp;</span><span style="color:#bbb"> </span><span style="color:#666">!</span>feed.<span style="color:#4070a0">entries</span>.<span style="color:#4070a0">isEmpty</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>summary<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>feed.<span style="color:#4070a0">entries</span>.<span style="color:#4070a0">get</span>(0).<span style="color:#4070a0">summary</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>PromptMessage.<span style="color:#4070a0">withUserRole</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>String.<span style="color:#4070a0">format</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                Please summarize this paper abstract (ID: %s):
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                %s&#34;&#34;&#34;</span>,<span style="color:#bbb"> </span>id,<span style="color:#bbb"> </span>summary));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>PromptMessage.<span style="color:#4070a0">withUserRole</span>(<span style="color:#4070a0">&#34;Error: Paper not found&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>The method returns a <code>PromptMessage</code> with the <strong>user</strong> role, as it is a prompt for the user.</p>
<p>This is a simple prompt that summarizes the paper&rsquo;s abstract (a summary of a summary).
Abstracts can be overly <em>scientific</em> and hard for non-experts to decipher.
However, this simple prompt usually yields easy-to-understand summaries.
Of course, you might instead retrieve the whole paper and create a much more elaborate summary that analyzes the whole content instead of just the abstract.</p>
<p>Perhaps more interesting is the prompt I defined to help craft search queries:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Prompt</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>name<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;construct_search_query&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Helper to construct an arXiv search query&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>PromptMessage<span style="color:#bbb"> </span><span style="color:#06287e">constructSearchQuery</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@PromptArg</span>(description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Topic or keywords&#34;</span>)<span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>topic,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@PromptArg</span>(description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Author name&#34;</span>)<span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>author,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@PromptArg</span>(description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Category code (e.g. cs.AI)&#34;</span>)<span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>category,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@PromptArg</span>(description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Year (e.g. 2024)&#34;</span>)<span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>year)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>I&rsquo;m just showing the signature here, but notice that this time I used <code>@PromptArg</code> annotations to give more details about each argument.</p>
<h2 id="configuration-in-gemini-cli">Configuration in Gemini CLI</h2>
<p>For using this MCP server in your favorite MCP client, you&rsquo;ll have to configure it to point at this Quarkus application.
MCP servers can be either local <em>STDIO</em> servers that run along your application (they are actually launched by your client, and use standard in and out for communication),
or they can be <em>remote</em> by using a <em>Streamable HTTP</em> transport mechanism (the server could be running locally as well, or be deployed in the cloud).</p>
<p>One cool thing with the Quarkus MCP extension is that choosing between STDIO and Streamable HTTP is <strong>just</strong> a build dependency change.
And if you want, you can build your MCP server to support both transports by using both dependencies in your build.
Here, for example with Maven&rsquo;s <code>pom.xml</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>io.quarkiverse.mcp<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>quarkus-mcp-server-http<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>1.8.1<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>io.quarkiverse.mcp<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>quarkus-mcp-server-stdio<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>1.8.1<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div><p>For coding and plenty other automation tasks, I tend to use <a href="https://geminicli.com/">Gemini CLI</a>,
but the syntax should be similar for your favorite chat / coding agent.</p>
<p>In development mode, I was actually running my MCP server as a Streamable HTTP server on the same host.
So I was simply running Quarkus in development mode with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>./mvnw quarkus:dev
</span></span></code></pre></div><p>And then I pointed the Gemini CLI at the local URL in my <code>~/.gemini/settings.json</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;mcpServers&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;arxiv&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;httpUrl&#34;</span>: <span style="color:#4070a0">&#34;http://localhost:8080/mcp&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>But once I&rsquo;m happy with the development, I install the application in my Maven local repository (or elsewhere),
and then I configure the MCP server to point at the absolute path where the JAR was installed:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;mcpServers&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;arxiv&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;command&#34;</span>: <span style="color:#4070a0">&#34;java&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;args&#34;</span>: [<span style="color:#4070a0">&#34;-jar&#34;</span>, <span style="color:#4070a0">&#34;/absolute/path/to/quarkus-run.jar&#34;</span>]
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Of course, if I decided to deploy my arXiv server to <a href="https://cloud.run/">Cloud Run</a>, for example, I would use the <code>httpUrl</code> parameter configuration approach instead.</p>
<h2 id="lets-search-papers">Let&rsquo;s Search Papers!</h2>
<p>When I run the <code>/mcp list</code> command inside Gemini CLI, I see my tools, resources, and prompts are properly exposed:</p>
<p><figure>
  <a href="#img-1627e2c28242d78d8c53881352983576">
    <img src="/img/gemini-cli/mcp-server/arxiv-mcp-0.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-1627e2c28242d78d8c53881352983576">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/mcp-server/arxiv-mcp-0.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>

    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>Resource templates are currently not supported by Gemini CLI, so they don&rsquo;t <em>yet</em> appear, but soon will, hopefully! Stay tuned.</p>
      </div>
    </div><p>I asked <code>what are the latest 10 papers in artificial intelligence (sorted by publication date)?</code> and you can see that it invoked my arXiv server
and its <code>search_papers</code> tool with the following parameters: <code>{&quot;sortOrder&quot;: &quot;descending&quot;, &quot;sortBy&quot;: &quot;lastUpdatedDate&quot;, &quot;query&quot;: &quot;cat:cs.AI&quot;, &quot;maxResults&quot;: 10}</code>.</p>
<p><figure>
  <a href="#img-2e044359d3243fdf677f6c9a236fd9c2">
    <img src="/img/gemini-cli/mcp-server/arxiv-mcp-1.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-2e044359d3243fdf677f6c9a236fd9c2">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/mcp-server/arxiv-mcp-1.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Then Gemini synthesized a human-readable interpretation of those JSON search results:</p>
<p><figure>
  <a href="#img-f21b2f52560435f7023e2327f2b538fb">
    <img src="/img/gemini-cli/mcp-server/arxiv-mcp-2.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-f21b2f52560435f7023e2327f2b538fb">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/mcp-server/arxiv-mcp-2.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I asked for more details about #2 and #7 of the list with this query: <code>I'd like to learn more about #2 and #7</code></p>
<p><figure>
  <a href="#img-9c8daf1d0e601b78a3c21c08ecbd36ed">
    <img src="/img/gemini-cli/mcp-server/arxiv-mcp-3.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-9c8daf1d0e601b78a3c21c08ecbd36ed">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/mcp-server/arxiv-mcp-3.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>And the <code>get_paper_details</code> MCP tool was invoked with the paper IDs (<code>{&quot;ids&quot;: [&quot;2601.10702&quot;, &quot;2601.10679&quot;]}</code>):</p>
<p><figure>
  <a href="#img-93b23d7460b17680f7ba18ae171a0e81">
    <img src="/img/gemini-cli/mcp-server/arxiv-mcp-4.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-93b23d7460b17680f7ba18ae171a0e81">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/mcp-server/arxiv-mcp-4.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Finally Gemini gave me a bit more context about those two papers:</p>
<p>And voila!</p>
<h2 id="summary">Summary</h2>
<p>In this post, we&rsquo;ve seen how the combination of <strong>Quarkus</strong> and its <strong>MCP extension</strong>
makes it straightforward to build a feature-complete MCP server in <strong>Java</strong>.
By leveraging annotations, we easily exposed not just <strong>tools</strong>, but also <strong>resources</strong>
(for taxonomy and metadata) and <strong>prompts</strong> (to guide the user), providing a rich context for any AI agent.</p>
<p>The development process was also a great example of <strong>AI-assisted productivity</strong>:
using <strong>Antigravity</strong> to scaffold the project and handle the integration with the arXiv API significantly sped up the implementation.
Whether you choose to run your server via <strong>STDIO</strong> for local use or <strong>HTTP</strong> for remote access,
the Model Context Protocol opens up exciting possibilities for making your data and services &ldquo;AI-ready.&rdquo;</p>
<p>Feel free to explore the <a href="https://github.com/glaforge/arxiv-mcp-server">source code on GitHub</a> and start building your own MCP servers!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>On AI Standards and Protocols: Focus on MCP and A2A</title><link>https://glaforge.dev/talks/2026/01/16/on-ai-standards-and-protocols-focus-on-mcp-and-a2a/</link><pubDate>Fri, 16 Jan 2026 11:04:42 +0100</pubDate><guid>https://glaforge.dev/talks/2026/01/16/on-ai-standards-and-protocols-focus-on-mcp-and-a2a/</guid><description>&lt;p>At &lt;a href="https://snowcamp.io/">SnowCamp 2026&lt;/a>,
with my &lt;a href="https://lescastcodeurs.com/">Cast Codeurs&lt;/a>
buddy &lt;a href="https://x.com/emmanuelbernard">Emmanuel Bernard&lt;/a>
of &lt;a href="https://hexactgon.com/">Hexactgon&lt;/a>,
I had the chance to deliver a talk on AI standards and protocols,
with a big focus on &lt;a href="https://modelcontextprotocol.io/docs/getting-started/intro">MCP&lt;/a> (Model Context Protocol),
and &lt;a href="https://a2a-protocol.org/latest/">A2A&lt;/a> (Agent 2 Agent Protocol).&lt;/p>
&lt;p>Without further ado, here&amp;rsquo;s the slide deck we presented:&lt;/p>
&lt;script async class="speakerdeck-embed" data-id="c93dff0ec41f47a693a02b9c2402d189" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js">&lt;/script>
&lt;p>This talk is based on the Devoxx 2025
&lt;a href="https://m.devoxx.com/events/dvbe25/talks/24587/on-standards-and-ai-agents-a-walkthrough-of-mcp-a2a-adk-and-more">deep dive session&lt;/a>
that I delivered with Emmanuel and my colleague &lt;a href="https://atamel.dev/">Mete Atamel&lt;/a>.
As the talk wasn&amp;rsquo;t recorded during SnowCamp, I&amp;rsquo;ll share with you the 3h-long video from Devoxx below:&lt;/p></description><content:encoded>
<![CDATA[<p>At <a href="https://snowcamp.io/">SnowCamp 2026</a>,
with my <a href="https://lescastcodeurs.com/">Cast Codeurs</a>
buddy <a href="https://x.com/emmanuelbernard">Emmanuel Bernard</a>
of <a href="https://hexactgon.com/">Hexactgon</a>,
I had the chance to deliver a talk on AI standards and protocols,
with a big focus on <a href="https://modelcontextprotocol.io/docs/getting-started/intro">MCP</a> (Model Context Protocol),
and <a href="https://a2a-protocol.org/latest/">A2A</a> (Agent 2 Agent Protocol).</p>
<p>Without further ado, here&rsquo;s the slide deck we presented:</p>
<script async class="speakerdeck-embed" data-id="c93dff0ec41f47a693a02b9c2402d189" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>This talk is based on the Devoxx 2025
<a href="https://m.devoxx.com/events/dvbe25/talks/24587/on-standards-and-ai-agents-a-walkthrough-of-mcp-a2a-adk-and-more">deep dive session</a>
that I delivered with Emmanuel and my colleague <a href="https://atamel.dev/">Mete Atamel</a>.
As the talk wasn&rsquo;t recorded during SnowCamp, I&rsquo;ll share with you the 3h-long video from Devoxx below:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/DiZs--ODXVM?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<h2 id="abstract">Abstract</h2>
<blockquote>
<p>AI agent foundations are built over a handful of common protocols that you need to master,
to make the best out of your LLM and agent framework. That’s why it’s important to understand them.
But some are catching up, others are not.</p>
<p>In this deep dive, we will explore the ecosystem showing you these standards and focusing on the important ones.
Knowing some of the frameworks is useful too to get started faster.
Welcome MCP, A2A, ACP protocols, and ADK, Arc, Quarkus, LangChain4j frameworks!</p>
<p>After giving you an overview of the main standards and protocols, their merit and their popularity,
we will start by building an agent using Agent Development Kit (ADK) and walk through making a tool call.
From there, zooming on MCP, we’ll see how to standardize that tool via a local MCP server
and then deploy it as a remote MCP server to share it with others.</p>
<p>Next, we’ll dive into the A2A protocol and enable our agent to participate in multi-agent conversations.
And to do that, we will use another framework, Quarkus and LangChain4j, showing how different stacks interact seamlessly through A2A.</p>
<p>You’ll learn not just what these protocols do and how they work, but why they matter, with detailed walkthroughs and live demos throughout.</p>
<p>If you’re struggling to understand all the protocol details around AI agents, this session is for you!</p></blockquote>
<h2 id="important-links">Important links</h2>
<p>Throughout the presentation, we showed various demos, implemented in Java:</p>
<h3 id="mcp">MCP</h3>
<ul>
<li><a href="https://github.com/glaforge/mn-mcp-server">Micronaut moon phases MCP server</a></li>
<li><a href="https://github.com/glaforge/moon-phases-quarkus-mcp-sse-server">Quarkus moon phases MCP server</a></li>
<li><a href="https://github.com/glaforge/arxiv-mcp-server">arXiv papers MCP server</a></li>
<li><a href="https://github.com/meteatamel/genai-beyond-basics">Python samples from Mete Atamel</a> showed at Devoxx</li>
</ul>
<h3 id="a2a">A2A</h3>
<ul>
<li><a href="https://github.com/glaforge/ai-agent-protocols">My repository with the product marketing MCP+A2A demo and other ADK samples</a> (showed at Devoxx)</li>
<li><a href="https://github.com/emmanuelbernard/quarkus-a2a-deepdive/tree/snowcamp-2026">Emmanuel&rsquo;s Quarkus A2A demo</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Building a Research Assistant with the Interactions API in Java</title><link>https://glaforge.dev/posts/2026/01/03/building-a-research-assistant-with-the-interactions-api-in-java/</link><pubDate>Sat, 03 Jan 2026 11:59:37 +0100</pubDate><guid>https://glaforge.dev/posts/2026/01/03/building-a-research-assistant-with-the-interactions-api-in-java/</guid><description>&lt;p>First of all, dear readers, let me wish you a &lt;strong>happy new year&lt;/strong>!
This is my first post on this blog for 2026.
I&amp;rsquo;m looking forward to continuing sharing interesting content with you.&lt;/p>
&lt;p>During my holiday break, I wanted to put my recent
&lt;a href="https://glaforge.dev/posts/2025/12/15/implementing-the-interactions-api-with-antigravity/">Java implementation&lt;/a>
of the Gemini &lt;a href="https://developers.googleblog.com/building-agents-with-the-adk-and-the-new-interactions-api/">Interactions API&lt;/a>
to the test. I implemented and released it with the help of &lt;a href="https://antigravity.google/">Antigravity&lt;/a>.
My colleague &lt;a href="https://x.com/Saboo_Shubham_">Shubham Saboo&lt;/a> and Gargi Gupta wrote a tutorial on
how to &lt;a href="https://www.theunwindai.com/p/build-an-ai-research-agent-with-google-interactions-api-gemini-3">build an AI research agent with Google Interactions API &amp;amp; Gemini 3&lt;/a>.
I thought this was a great opportunity to replicate this example in Java
using my &lt;a href="https://github.com/glaforge/gemini-interactions-api-sdk/">Interactions API Java SDK&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>First of all, dear readers, let me wish you a <strong>happy new year</strong>!
This is my first post on this blog for 2026.
I&rsquo;m looking forward to continuing sharing interesting content with you.</p>
<p>During my holiday break, I wanted to put my recent
<a href="https://glaforge.dev/posts/2025/12/15/implementing-the-interactions-api-with-antigravity/">Java implementation</a>
of the Gemini <a href="https://developers.googleblog.com/building-agents-with-the-adk-and-the-new-interactions-api/">Interactions API</a>
to the test. I implemented and released it with the help of <a href="https://antigravity.google/">Antigravity</a>.
My colleague <a href="https://x.com/Saboo_Shubham_">Shubham Saboo</a> and Gargi Gupta wrote a tutorial on
how to <a href="https://www.theunwindai.com/p/build-an-ai-research-agent-with-google-interactions-api-gemini-3">build an AI research agent with Google Interactions API &amp; Gemini 3</a>.
I thought this was a great opportunity to replicate this example in Java
using my <a href="https://github.com/glaforge/gemini-interactions-api-sdk/">Interactions API Java SDK</a>.</p>
<p>A picture is often worth a thousand words, so let&rsquo;s have a look at the key components of our research agent workflow:</p>
<p><figure>
  <a href="#img-a557bc150ed9e4d9cf74a179dc6906cf">
    <img src="/img/gemini/interactions/research_agent_workflow_infographic.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-a557bc150ed9e4d9cf74a179dc6906cf">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/interactions/research_agent_workflow_infographic.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>The goal of this tutorial is to build a research assistant, split into 4 key phases:</p>
<ul>
<li>Phase &#x31;&#xfe0f;&#x20e3; : <strong>Planning</strong> — Given a topic to research, aided by the Google Search tool, Gemini 3 Flash defines different research tasks related to the topic.</li>
<li>Phase &#x32;&#xfe0f;&#x20e3; : <strong>Research</strong> — The Deep Research model will be launched as a background task to research the different topic areas defined in the planning phase.</li>
<li>Phase &#x33;&#xfe0f;&#x20e3; : <strong>Synthesis</strong> — This time, we use the more powerful Gemini 3 Pro to do the synthesis of the research report.</li>
<li>Phase &#x34;&#xfe0f;&#x20e3; : <strong>Infographic</strong> — Last but not least, we&rsquo;ll use &#x1f34c; Nano Banana Pro (aka Gemini 3 Pro Image) to generate an infographic about this research.</li>
</ul>
<h2 id="lets-implement-this-research-workflow">Let&rsquo;s Implement this Research Workflow!</h2>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Information</span>
      </div>
      <div class="admonition-content">
        <p>You&rsquo;ll find the entire source code for this <a href="https://github.com/glaforge/gemini-interactions-api-sdk/blob/main/src/test/java/io/github/glaforge/gemini/interactions/ResearchAgentTest.java">example in my GitHub repository</a>.</p>
      </div>
    </div><p>This all starts with the planning phase, using Gemini 3 Flash:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Step 0: Define the research goal</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>researchGoal<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Research the current state of Quantum Computing in 2025,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    specifically looking for major breakthroughs in error correction.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// --- Phase 1: Plan ---</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Gemini 3 Flash Preview creates research tasks</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ModelInteractionParams<span style="color:#bbb"> </span>planParams<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ModelInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-3-flash-preview&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">input</span>(String.<span style="color:#4070a0">format</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Create a numbered research plan for: %s
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Format: 1. [Task] - [Details]
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Include 3 specific tasks.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;&#34;&#34;</span>,<span style="color:#bbb"> </span>researchGoal))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">tools</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GoogleSearch())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">store</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Launch the request</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Interaction<span style="color:#bbb"> </span>planInteraction<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">create</span>(planParams);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Retrieve the response with text and interaction ID</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>planText<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>getText(planInteraction);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>planId<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>planInteraction.<span style="color:#4070a0">id</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>The goal is to research information about the latest breakthroughs in Quantum Computing over the past year.
We create an <em>interaction</em> that asks Gemini 3 Flash to define a few research tasks, following a specific format.</p>
<p>Notice that we provide the built-in <code>GoogleSearch</code> tool so the model can search the internet when defining those tasks.</p>
<p>We set <code>store(true)</code> to save the interaction on the server-side,
and we save the interaction ID for later reuse, ensuring subsequent interactions continue the same discussion.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Utility method to extract the LLM generated tasks</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>List<span style="color:#666">&lt;</span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>tasks<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>parseTasks(planText);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// --- Phase 2: Research ---</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Select tasks and run Deep Research Agent</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// In this test, we select all tasks.</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>selectedTasks<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>String.<span style="color:#4070a0">join</span>(<span style="color:#4070a0">&#34;\n\n&#34;</span>,<span style="color:#bbb"> </span>tasks);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>AgentInteractionParams<span style="color:#bbb"> </span>researchParams<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>AgentInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">agent</span>(<span style="color:#4070a0">&#34;deep-research-pro-preview-12-2025&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">input</span>(String.<span style="color:#4070a0">format</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#4070a0">&#34;Research these tasks thoroughly with sources:\n\n%s&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>selectedTasks))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">previousInteractionId</span>(planId)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">background</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">store</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Interaction<span style="color:#bbb"> </span>researchInteraction<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">create</span>(researchParams);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>researchId<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>researchInteraction.<span style="color:#4070a0">id</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>researchText<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>getText(researchInteraction);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Wait for completion up to 10 mins as deep research can be slow</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>researchInteraction<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>waitForCompletion(client,<span style="color:#bbb"> </span>researchId,<span style="color:#bbb"> </span>600);<span style="color:#bbb">
</span></span></span></code></pre></div><p>In this second phase, I use a few utility methods to parse the tasks generated in the previous phase
(I should probably use structured output for that at some point)
and to wait for the completion of the background tasks.</p>
<p>A few interesting points here:</p>
<ul>
<li>We reuse the interaction ID from the previous phase via <code>previousInteractionId()</code>, taking advantage of the stateful nature of the Interactions API.
This allows the research agent to maintain context from the planning phase, in addition to the specific tasks generated.</li>
<li>We specify that the task should run in the background with <code>background(true)</code>.</li>
<li>Finally, we poll for the completion of the Deep Research agent, which can take several minutes.</li>
</ul>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// --- Phase 3: Synthesis ---</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ModelInteractionParams<span style="color:#bbb"> </span>synthesisParams<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ModelInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-3-pro-preview&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">input</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Create executive report with Summary, Findings,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Recommendations, Risks based on the research.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">previousInteractionId</span>(researchId)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">store</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Interaction<span style="color:#bbb"> </span>synthesisInteraction<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">create</span>(synthesisParams);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>synthesisText<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>getText(synthesisInteraction);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Phase 3 is a simple call to Gemini 3 Pro to synthesize the research report.
Again, we store the session and reuse the previous interaction ID.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// --- Phase 4: Infographic ---</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ModelInteractionParams<span style="color:#bbb"> </span>infographicParams<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ModelInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-3-pro-image-preview&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">input</span>(<span style="color:#4070a0">&#34;Create a whiteboard summary infographic for the following: \n\n&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#666">+</span><span style="color:#bbb"> </span>synthesisText)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">responseModalities</span>(List.<span style="color:#4070a0">of</span>(Modality.<span style="color:#4070a0">IMAGE</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Interaction<span style="color:#bbb"> </span>infographicInteraction<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">create</span>(infographicParams);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>saveInfographic(infographicInteraction);<span style="color:#bbb">
</span></span></span></code></pre></div><p>The last phase is the infographic generation, using &#x1f34c; Nano Banana Pro.
We pass the synthesis from the previous phase.
We don&rsquo;t need to reuse the interaction ID here, as the synthesis itself provides enough context for the infographic.</p>

    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Reminder</span>
      </div>
      <div class="admonition-content">
        <p>You can check
<a href="https://github.com/glaforge/gemini-interactions-api-sdk/blob/main/src/test/java/io/github/glaforge/gemini/interactions/ResearchAgentTest.java">the entire source code on GitHub</a>.</p>
      </div>
    </div><h2 id="the-outcome">The Outcome</h2>
<p>I won&rsquo;t include the full output, but I&rsquo;d like to highlight the impressive infographic generated by Nano Banana Pro as a result of this research plan:</p>
<p><figure>
  <a href="#img-54823401c91d43e20b06a4c058df3c95">
    <img src="/img/gemini/interactions/quantum-infographic.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-54823401c91d43e20b06a4c058df3c95">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/interactions/quantum-infographic.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I hope this makes sense to those of you knowledgeable about Quantum Computing.
While I&rsquo;m not an expert in the field, I&rsquo;m really fond of the infographics that &#x1f34c; Nano Banana can generate, with their sharp and crisp text.</p>
<h2 id="whats-to-like">What&rsquo;s to Like?</h2>
<p>What I particularly like about the Interactions API is that it handles state on the server-side.
It&rsquo;s a departure from the traditional stateless LLM conversations where frameworks must pass the entire history at each round.
Even unrelated LLM requests or agent tasks can share the same <em>session</em> by reusing the interaction ID.</p>
<p>Additionally, I&rsquo;m happy that my <a href="https://github.com/glaforge/gemini-interactions-api-sdk/">Java SDK</a>
for the Interactions API works well for more involved use cases, validating its capabilities.
Until the Gemini <em>unified</em> <a href="https://docs.cloud.google.com/vertex-ai/generative-ai/docs/sdks/overview">SDK</a>
supports the Interaction API, I&rsquo;ll definitely be sticking with my own!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Implementing the Interactions API with Antigravity</title><link>https://glaforge.dev/posts/2025/12/15/implementing-the-interactions-api-with-antigravity/</link><pubDate>Mon, 15 Dec 2025 10:40:48 +0100</pubDate><guid>https://glaforge.dev/posts/2025/12/15/implementing-the-interactions-api-with-antigravity/</guid><description>&lt;p>Google and DeepMind have announced the &lt;strong>Interactions API&lt;/strong>, a new way to interact with Gemini models and agents.&lt;/p>
&lt;p>Here are some useful links to learn more about this new API:&lt;/p>
&lt;ul>
&lt;li>An announcement is available on Google&amp;rsquo;s Keywords blog:&lt;br />
&lt;a href="https://blog.google/technology/developers/interactions-api/">Interactions API: A unified foundation for models and agents&lt;/a>&lt;/li>
&lt;li>A more detailed article is available on Google&amp;rsquo;s developers blog:&lt;br />
&lt;a href="https://developers.googleblog.com/building-agents-with-the-adk-and-the-new-interactions-api/">Building agents with the ADK and the new Interactions API&lt;/a>&lt;/li>
&lt;li>The newly released Gemini &lt;strong>Deep Research agent&lt;/strong> is now available via the Interactions API as well:&lt;br />
&lt;a href="https://blog.google/technology/developers/deep-research-agent-gemini-api/">Build with Gemini Deep Research&lt;/a>&lt;/li>
&lt;li>The official &lt;a href="https://ai.google.dev/gemini-api/docs/interactions">documentation of the Interactions API&lt;/a>.&lt;/li>
&lt;/ul>
&lt;h2 id="about-the-interactions-api">About the Interactions API&lt;/h2>
&lt;h3 id="the-rationale-and-motivation">The Rationale and Motivation&lt;/h3>
&lt;p>The Interactions API was introduced to address a shift in AI development, moving from simple,
stateless text generation to more complex, multi-turn &lt;em>agentic&lt;/em> workflows.
It serves as a dedicated interface for systems that require memory, reasoning, and tool use.
It provides a unified interface for both simple LLM calls and more complex agent calls.&lt;/p></description><content:encoded>
<![CDATA[<p>Google and DeepMind have announced the <strong>Interactions API</strong>, a new way to interact with Gemini models and agents.</p>
<p>Here are some useful links to learn more about this new API:</p>
<ul>
<li>An announcement is available on Google&rsquo;s Keywords blog:<br />
<a href="https://blog.google/technology/developers/interactions-api/">Interactions API: A unified foundation for models and agents</a></li>
<li>A more detailed article is available on Google&rsquo;s developers blog:<br />
<a href="https://developers.googleblog.com/building-agents-with-the-adk-and-the-new-interactions-api/">Building agents with the ADK and the new Interactions API</a></li>
<li>The newly released Gemini <strong>Deep Research agent</strong> is now available via the Interactions API as well:<br />
<a href="https://blog.google/technology/developers/deep-research-agent-gemini-api/">Build with Gemini Deep Research</a></li>
<li>The official <a href="https://ai.google.dev/gemini-api/docs/interactions">documentation of the Interactions API</a>.</li>
</ul>
<h2 id="about-the-interactions-api">About the Interactions API</h2>
<h3 id="the-rationale-and-motivation">The Rationale and Motivation</h3>
<p>The Interactions API was introduced to address a shift in AI development, moving from simple,
stateless text generation to more complex, multi-turn <em>agentic</em> workflows.
It serves as a dedicated interface for systems that require memory, reasoning, and tool use.
It provides a unified interface for both simple LLM calls and more complex agent calls.</p>
<p>If you&rsquo;ve used the Gemini API before, the standard operation (<code>generateContent</code>) was designed for simple request-response tasks.
It was stateless, requiring you to send the entire conversation history with each new question.
As models evolved to incorporate <em>&ldquo;thinking&rdquo;</em> processes and advanced tool use (e.g., built-in tools, sequential and parallel function calls),
the classic API approach required extra fields to manage state, such as <em>&ldquo;thought signatures&rdquo;</em>.
This new interface and endpoint support both raw models (like Gemini 3 Pro) and fully managed agents
(like the Gemini <a href="https://ai.google.dev/gemini-api/docs/deep-research">Deep Research Agent</a>).</p>
<h3 id="the-key-advantages">The Key Advantages</h3>
<p>It simplifies state and context management through <strong>server-side history</strong>, native handling of agent <em>&ldquo;thoughts&rdquo;</em> and data schemas.
It supports <strong>background processing</strong> for long-running tasks (in particular for agents).
Furthermore, interoperability with Agent Development Kit (ADK) and Agent2Agent (A2A) protocol is ongoing.
Finally, it offers advanced capabilities such as tooling (including Google Search and Code execution),
structured JSON outputs, Model Context Protocol (MCP) support, and native multimodal handling.</p>
<h2 id="implementing-the-interactions-api-with-antigravity">Implementing the Interactions API with Antigravity</h2>
<h3 id="a-few-words-about-antigravity">A Few Words About Antigravity</h3>
<p>I decided to put <a href="https://antigravity.google/">Antigravity</a>, Google&rsquo;s new agentic development environment, to the test,
by pointing Antigravity at the Open API 3
<a href="https://ai.google.dev/static/api/interactions.openapi.json">specification of the Interactions API</a>,
and iterating with it to come up with a <strong>Java implementation</strong>.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Learning more about Antigravity</span>
      </div>
      <div class="admonition-content">
        <p>You can read more about the key aspects of Antigravity in my colleague, Mete Atamel&rsquo;s article
<a href="https://atamel.dev/posts/2025/12-01_antigravity_editor_tips/">introducing Antigravity and tips and tricks</a>,
as well as his follow-up article on
<a href="https://atamel.dev/posts/2025/11-25_customize_antigravity_rules_workflows/">how to customize Antigravity with rules and workflows</a>.
And my other colleague, Romin Irani, wrote a great <a href="https://codelabs.developers.google.com/getting-started-google-antigravity#3">codelab</a>
to get you started.</p>
      </div>
    </div><p>What I find interesting with Antigravity is that it shifts the developer&rsquo;s perspective.
We&rsquo;re used to having the code editor as our central point of focus and action.
But with Antigravity (advertised as an <em>agentic development platform</em>), the main point of entry is the <strong>agent manager</strong>.</p>
<p>The agent manager has an <strong>inbox</strong>, with the various ongoing implementation tasks.</p>
<p><figure>
  <a href="#img-31e58f9bbf9b63f6ec7b815ffcc216ca">
    <img src="/img/antigravity/inbox.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-31e58f9bbf9b63f6ec7b815ffcc216ca">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/antigravity/inbox.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>You have <strong>workspaces</strong> for the different projects you&rsquo;re working on.</p>
<p>There&rsquo;s a <strong>playground</strong> to test ideas, that you can then convert to a proper workspace should the experiment become serious.
This is actually through the playground that I started experimenting with the Interactions API.</p>
<p><figure>
  <a href="#img-a8675a1dbfc9c6bff928b0e483454ebf">
    <img src="/img/antigravity/playground.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-a8675a1dbfc9c6bff928b0e483454ebf">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/antigravity/playground.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Fear not, you can always switch back to the code editor (a fork of VS Code)!
That&rsquo;s also where you&rsquo;ll be able to approve/reject code changes suggested by Antigravity.</p>
<p><figure>
  <a href="#img-b32a89987137f750b95cdc3470202ad3">
    <img src="/img/antigravity/editor.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-b32a89987137f750b95cdc3470202ad3">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/antigravity/editor.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Another important aspect of Antigravity is that you start with a prompt explaining the task at hand,
potentially adding all sorts of context (like screenshots or documents).
Then Antigravity is going to create an <strong>implementation plan</strong> that you can comment and review like in a Google Docs.</p>
<p><figure>
  <a href="#img-666f0e57e2d08e917051ce42d69de7f4">
    <img src="/img/antigravity/implementation-plan.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-666f0e57e2d08e917051ce42d69de7f4">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/antigravity/implementation-plan.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Once you&rsquo;re happy with the plan, Antigravity will start working for you.
Depending on configuration and task complexity, it might request you to approve changes, tool usage, etc.</p>
<p>Once the task is accomplished, Antigravity will show you a <strong>walkthrough</strong> to guide you through the implementation.
And as always, it&rsquo;s still possible to review it, and ask for further modifications or improvements.</p>
<p><figure>
  <a href="#img-021c4eb8954d2bebeb164b7c7f94ae06">
    <img src="/img/antigravity/walkthrough.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-021c4eb8954d2bebeb164b7c7f94ae06">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/antigravity/walkthrough.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>You can also view the <strong>task list</strong> of all the incremental steps Antigravity went through to implement your requests.</p>
<p><figure>
  <a href="#img-6fe857ff5b2d58183ede2c04d018eddd">
    <img src="/img/antigravity/task-list.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-6fe857ff5b2d58183ede2c04d018eddd">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/antigravity/task-list.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h3 id="color-me-impressed">Color Me Impressed!</h3>
<p>Honestly, I was quite impressed with Antigravity and Gemini 3 Pro.
It successfully implemented an elegant Java API based on the Interactions API&rsquo;s Open API specification.
Then I iterated with Antigravity to further tweak it to my liking, to add tests, to help me deploy the project to Maven Central.
Not only it was good at coding and following a plan,
but it was very helpful on the command-line for running the build, the deployment commands, etc.</p>
<h2 id="now-lets-interact">Now Let&rsquo;s Interact!</h2>
<p>Antigravity helped me publish my Java implementation of the Interactions API
to <a href="https://central.sonatype.com/artifact/io.github.glaforge/gemini-interactions-api-sdk">Maven Central</a>,
guiding me through the creation of public/private keys and the Maven commands required to prepare and perform the release.</p>

    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>You can find my <a href="https://github.com/glaforge/gemini-interactions-api-sdk/">implementation on GitHub</a>.
Have a look at the <a href="https://github.com/glaforge/gemini-interactions-api-sdk/blob/main/README.md">README</a> for usage details, but we&rsquo;ll go through them together in this article.</p>
      </div>
    </div><h2 id="setup-and-authentication">Setup and Authentication</h2>
<p>In your Java project&rsquo;s build file, you&rsquo;ll need to specify the dependency to my SDK.</p>
<p>In your <code>pom.xml</code> if you&rsquo;re building with Maven:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>io.github.glaforge<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>gemini-interactions-api-sdk<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>0.3.0<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div><p>And in your <code>build.gradle</code> if you&rsquo;re building with Gradle:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>implementation <span style="color:#4070a0">&#39;io.github.glaforge:gemini-interactions-api-sdk:0.3.0&#39;</span>
</span></span></code></pre></div><p>And you should export a Gemini API key (that you can <a href="https://ai.google.dev/gemini-api/docs/api-key">get in Google AI Studio</a>) as an environment variable:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007020">export</span> <span style="color:#bb60d5">GEMINI_API_KEY</span><span style="color:#666">=</span>YOUR_API_KEY
</span></span></code></pre></div><p>Now you&rsquo;re ready to interact!</p>
<h2 id="your-first-interaction">Your First Interaction</h2>
<p>Instead of just sending a prompt string, we create an <code>Interaction</code> object.
We specify the kind (the model or agent we want to talk to) and the parameters (our prompt).</p>
<p>Let&rsquo;s make a simple call to Gemini (I&rsquo;ll spare you the <code>import</code>s):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Create a client with your API key</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>GeminiInteractionsClient<span style="color:#bbb"> </span>client<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GeminiInteractionsClient.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Create the interaction, choosing a model, and passing the prompt in input</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Interaction<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">create</span>(ModelInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">input</span>(<span style="color:#4070a0">&#34;Why is the sky blue?&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// The output is multimodal, so let&#39;s see if there&#39;s text, image, or thoughts in output</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>response.<span style="color:#4070a0">outputs</span>().<span style="color:#4070a0">forEach</span>((Content<span style="color:#bbb"> </span>output)<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">switch</span><span style="color:#bbb"> </span>(output)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span>TextContent<span style="color:#bbb"> </span>text<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(text.<span style="color:#4070a0">text</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span>ImageContent<span style="color:#bbb"> </span>image<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(image.<span style="color:#4070a0">data</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span>ThoughtContent<span style="color:#bbb"> </span>thought<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;Thought: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>thought.<span style="color:#4070a0">signature</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">default</span><span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;Unknown content type: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>output);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>});<span style="color:#bbb">
</span></span></span></code></pre></div><p>This is a synchronous call, so you&rsquo;ll be waiting for it to finish generating its answer.</p>
<h2 id="a-multi-turn-conversation">A Multi-turn conversation</h2>
<p>When not taking advantage of the statefulness of the Interactions API, you can still do multi-turn conversations:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ModelInteractionParams<span style="color:#bbb"> </span>request<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ModelInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">input</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Turn(USER,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Hello!&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Turn(MODEL,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Hi! How can I help?&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Turn(USER,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Tell me a joke&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Interaction<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">create</span>(request);<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="a-multimodal-request">A Multimodal request</h2>
<p>The Interactions API handles multimodal requests, mixing text, images, audio, videos, etc:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ModelInteractionParams<span style="color:#bbb"> </span>request<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ModelInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">input</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>TextContent(<span style="color:#4070a0">&#34;Describe this image&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// Create an image from Base64 string</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ImageContent(<span style="color:#4070a0">&#34;BASE64_STRING...&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;image/png&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Interaction<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">create</span>(request);<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="creating-an-image-with-nano-banana-pro-banana">Creating an Image with Nano Banana Pro &#x1f34c;</h2>
<p>I&rsquo;m a big fan of the Nano Banana model for creating and editing images.
You can easily invoke it as well:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ModelInteractionParams<span style="color:#bbb"> </span>request<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ModelInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-3-pro-image-preview&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">input</span>(<span style="color:#4070a0">&#34;Create an infographic about blood, organs, and the circulatory system&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseModalities</span>(Modality.<span style="color:#4070a0">IMAGE</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Interaction<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">create</span>(request);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>response.<span style="color:#4070a0">outputs</span>().<span style="color:#4070a0">forEach</span>(content<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(content<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">instanceof</span><span style="color:#bbb"> </span>ImageContent<span style="color:#bbb"> </span>image)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#902000">byte</span><span style="color:#666">[]</span><span style="color:#bbb"> </span>imageBytes<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Base64.<span style="color:#4070a0">getDecoder</span>().<span style="color:#4070a0">decode</span>(image.<span style="color:#4070a0">data</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// Save imageBytes to a file</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>});<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="function-calling">Function Calling</h2>
<p>You can pass tools to your model to let it request its use for achieving its goal.
This example is a little bit more involved, of course, as it sets up a method and handles the back and forth exchange:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// 1. Define the tool, its name, description, and input schema as simple Maps</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Function<span style="color:#bbb"> </span>weatherTool<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Function.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;get_weather&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;Get the current weather&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">parameters</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;type&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;object&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;properties&#34;</span>,<span style="color:#bbb"> </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;location&#34;</span>,<span style="color:#bbb"> </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;type&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;string&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;required&#34;</span>,<span style="color:#bbb"> </span>List.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;location&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// 2. Initial request with tools</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ModelInteractionParams<span style="color:#bbb"> </span>request<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ModelInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">input</span>(<span style="color:#4070a0">&#34;What is the weather in London?&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(weatherTool)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Interaction<span style="color:#bbb"> </span>interaction<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">create</span>(request);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// 3. Handle function call</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Content<span style="color:#bbb"> </span>lastOutput<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>interaction.<span style="color:#4070a0">outputs</span>().<span style="color:#4070a0">getLast</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(lastOutput<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">instanceof</span><span style="color:#bbb"> </span>FunctionCallContent<span style="color:#bbb"> </span>call)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(<span style="color:#4070a0">&#34;get_weather&#34;</span>.<span style="color:#4070a0">equals</span>(call.<span style="color:#4070a0">name</span>()))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>location<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>(String)<span style="color:#bbb"> </span>call.<span style="color:#4070a0">arguments</span>().<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;location&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// Execute local logic...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>weather<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Rainy, 15°C&#34;</span>;<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// Simulated result</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// 4. Send Function Result</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>ModelInteractionParams<span style="color:#bbb"> </span>continuation<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ModelInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#60a0b0;font-style:italic">// Passing previous interaction ID instead of the whole conversation</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">previousInteractionId</span>(interaction.<span style="color:#4070a0">id</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">input</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>FunctionResultContent(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#4070a0">&#34;function_result&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>call.<span style="color:#4070a0">id</span>(),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>call.<span style="color:#4070a0">name</span>(),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">false</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;weather&#34;</span>,<span style="color:#bbb"> </span>weather)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Interaction<span style="color:#bbb"> </span>finalResponse<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">create</span>(continuation);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(finalResponse.<span style="color:#4070a0">outputs</span>().<span style="color:#4070a0">getLast</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>My SDK is fairly bare-bone and doesn&rsquo;t handle automatic function calling like the Gemini SDK does, or LangChain4j, etc.
But it could be a possible enhancement.</p>
<p>In the code, notice how <strong>it handles the session state</strong>.
Instead of passing the whole conversation again when replying with the tool response,
we actually <strong>pass only the interaction ID of the previous call</strong>.
Hence, <strong>state is handled on the server side</strong>!</p>
<h3 id="deep-research">Deep Research</h3>
<p>An important aspect of the Interactions API is its ability to call agents, and not just models.
The <strong>Deep Research agent</strong> is the first to implement the Interactions API.
This is the research agent you may be familiar with from the Gemini web app, used to create long and detailed reports.
This time we&rsquo;re going to create an <em>agent interaction</em> instead of a <em>model interaction</em>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>AgentInteractionParams<span style="color:#bbb"> </span>request<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>AgentInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">agent</span>(<span style="color:#4070a0">&#34;deep-research-pro-preview-12-2025&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">input</span>(<span style="color:#4070a0">&#34;Research the history of the Google TPUs&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Interaction<span style="color:#bbb"> </span>interaction<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">create</span>(request);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Poll for completion</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">while</span><span style="color:#bbb"> </span>(interaction.<span style="color:#4070a0">status</span>()<span style="color:#bbb"> </span><span style="color:#666">!=</span><span style="color:#bbb"> </span>Status.<span style="color:#4070a0">COMPLETED</span>)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Thread.<span style="color:#4070a0">sleep</span>(1000);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>interaction<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">get</span>(interaction.<span style="color:#4070a0">id</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(interaction.<span style="color:#4070a0">outputs</span>());<span style="color:#bbb">
</span></span></span></code></pre></div><p>This is an asynchronous task, so you have to wait for its completion by polling.
You can also steer the output report generation by further customizing your prompt, suggesting a particular report structure.</p>
<h3 id="model-context-protocol">Model Context Protocol</h3>
<p>The Interactions API comes with built-in MCP (Model Context Protocol) support.
And by support, I mean that <strong>it handles the MCP call itself</strong>.</p>
<p>A few caveats to be aware of:</p>
<ul>
<li>Currently, it can only call <strong>remote MCP servers</strong> (not local STDIO ones),
and <strong>only Streamable HTTP servers</strong> are supported (Server-Sent Events are deprecated anyway).</li>
<li>Currently, it <strong>works with Gemini 2.5</strong>, but <em>not</em> with Gemini 3 Pro.</li>
<li>MCP server names shouldn&rsquo;t contain a <code>-</code> in their name,
as this character is reserved for namespacing function calls with the name of the server
(i.e. <code>moon_server-current_moon_phase</code> with <code>moon_server</code> being the server name, and <code>current_moon_phase</code> the function name.)
So favor underscores for clearer names.</li>
</ul>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// 1. Define the MCP Server tool</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>serverName<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;moon_server&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>serverUrl<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;https://mn-mcp-server-1029513523185.europe-west1.run.app/mcp&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Tool<span style="color:#bbb"> </span>mcpServer<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Tool.<span style="color:#4070a0">McpServer</span>(serverName,<span style="color:#bbb"> </span>serverUrl);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>List<span style="color:#666">&lt;</span>Tool<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>tools<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>List.<span style="color:#4070a0">of</span>(mcpServer);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// 2. Create Interaction</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ModelInteractionParams<span style="color:#bbb"> </span>createParams<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ModelInteractionParams.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">input</span>(<span style="color:#4070a0">&#34;What is the current phase of the moon?&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(tools)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// 3. Make the Request</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Interaction<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">create</span>(createParams);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// 4. Then Analyze the Response...</span><span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="wrapping-up">Wrapping up</h2>
<p>The <strong>Interactions API</strong> unifies the way we handle both fast inference (standard models) and slow thinking (agents).
By generating this SDK, and iterating on it with <strong>Antigravity</strong>,
I&rsquo;ve tried to make it as easy as possible to integrate these new capabilities into Java applications.</p>
<p>Note that this is not (yet?) a production-grade SDK; it&rsquo;s the result of a few hours of experimentation and would benefit from further refinement.
But I was very happy with the outcome, as I was able to quickly experiment with this new API in Java.</p>
<p>Check out the <a href="https://github.com/glaforge/gemini-interactions-api-sdk/blob/main/README.md">README</a> for more configuration details and examples.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>AI Agentic Patterns and Anti-Patterns</title><link>https://glaforge.dev/talks/2025/12/02/ai-agentic-patterns-and-anti-patterns/</link><pubDate>Tue, 02 Dec 2025 12:24:40 +0100</pubDate><guid>https://glaforge.dev/talks/2025/12/02/ai-agentic-patterns-and-anti-patterns/</guid><description>&lt;p>This week, I was on stage at the
&lt;a href="https://events.tech.rocks/e/tech-rocks-summit-2025/fr/session/9b0ac306-ac56-f011-8f7c-6045bd96bdd0/agents-ia-la-nouvelle-frontiere-des-llms">Tech Rocks Summit 2025&lt;/a>
in the beautiful Théâtre de Paris.
This is the first I&amp;rsquo;m attending this event, gathering a nice crowd of CTOs, tech leads, architects, and decision makers.&lt;/p>
&lt;p>My talk focused on what everyone is talking about right now: &lt;strong>AI Agents&lt;/strong>.
And in particular, I was interested in sharing with the audience things I&amp;rsquo;ve seen work or not work in companies, startups,
and via tons of discussions with AI practitioners I met at conferences, meetups, or customer meetings.&lt;/p></description><content:encoded>
<![CDATA[<p>This week, I was on stage at the
<a href="https://events.tech.rocks/e/tech-rocks-summit-2025/fr/session/9b0ac306-ac56-f011-8f7c-6045bd96bdd0/agents-ia-la-nouvelle-frontiere-des-llms">Tech Rocks Summit 2025</a>
in the beautiful Théâtre de Paris.
This is the first I&rsquo;m attending this event, gathering a nice crowd of CTOs, tech leads, architects, and decision makers.</p>
<p>My talk focused on what everyone is talking about right now: <strong>AI Agents</strong>.
And in particular, I was interested in sharing with the audience things I&rsquo;ve seen work or not work in companies, startups,
and via tons of discussions with AI practitioners I met at conferences, meetups, or customer meetings.</p>
<p>Without further ado, here&rsquo;s the deck in French &#x1f1eb;&#x1f1f7; I showed on stage:</p>
<script async class="speakerdeck-embed" data-id="ef0430dc8f2e418b8e0e2e8297cf452a" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>And in English as well for my international readers:</p>
<script async class="speakerdeck-embed" data-id="eebc232e3cc34b4b891ba483f5953c3d" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<h2 id="a-quick-historical-recap">A Quick Historical Recap</h2>
<p>We saw the <a href="https://en.wikipedia.org/wiki/Transformer_(deep_learning)">Transformer</a> wave in 2017, the ChatGPT tsunami in 2023,
and the <a href="https://glaforge.dev/tags/retrieval-augmented-generation/">RAG</a> (Retrieval Augmented Generation) trend in 2024.
In 2025, here we are: Agents are the new frontier for LLMs.</p>
<p>But concretely, what does this change for us, devs and tech leaders?
What works, what doesn&rsquo;t work?
Here are the key points of my presentation.</p>
<h2 id="what-is-an-agent-really">What is an Agent, Really?</h2>
<p>Forget the magic for two minutes. An agent is a fairly simple equation:</p>
<blockquote>
<p>Agent = LLM + Memory + Planning + Tools</p></blockquote>
<p>It is no longer just a model predicting the next word.
It is a system that observes, plans, acts, and thinks (the famous Reflection loop to correct its own errors).</p>
<h2 id="architecture-patterns-that-work">Architecture Patterns that Work</h2>
<p>I presented 4 patterns to avoid reinventing the wheel:</p>
<ul>
<li><strong>The Orchestrator</strong>: A supervisor agent that delegates to specialized <em>sub-agents</em>.
This is crucial for breaking down a complex task into digestible chunks.</li>
<li><strong>Rethinking Tools</strong>: Don&rsquo;t just throw your raw REST API at the LLM.
Create <em>&ldquo;business task&rdquo;</em> oriented tools (e.g., &ldquo;Schedule Meeting&rdquo; vs <code>POST /calendar/v1/events</code>).
Fewer tools = less confusion = more determinism.</li>
<li><strong>MCP (Model Context Protocol)</strong>: This is the future standard, essentially the <em>USB for AI tools</em>.
It standardizes how an agent connects to its tools, launched by Anthropic and now widely adopted (but still rapidly evolving).</li>
<li><strong>A2A (Agent to Agent)</strong>: Google and its partners are pushing this extensible protocol
so that agents can discover and collaborate with each other, regardless of their language or framework.</li>
</ul>
<h2 id="traps-to-avoid-anti-patterns">Traps to Avoid (Anti-Patterns)</h2>
<p>I insisted on this because I see teams falling into these traps:</p>
<ul>
<li><strong>The &ldquo;Chatbot Mandate&rdquo;</strong>: Does your leadership want <em>&ldquo;a chatbot&rdquo;</em>? Resist.
AI should often be invisible (like a Head-Up Display), not necessarily an endless conversation.</li>
<li><strong>Insufficient Vibe-Checking</strong>: <em>&ldquo;It looks like it works&rdquo;</em> is not a testing strategy.
You need <em>Golden Responses</em>, LLM-as-a-Judge, and a <strong>real evaluation phase</strong>.</li>
<li><strong>Silent Confabulation</strong>: RAG is great, but if the AI invents things, it&rsquo;s dangerous.
Force source citation and aim for IVO
(<em>Immediately Validatable Output</em>, coined by my colleague <a href="https://www.zackakil.com/">Zack Akil</a>):
the user must be able to verify the result at a glance.</li>
<li><strong>The Coding &ldquo;Rabbit Hole&rdquo;</strong>: Coding agents are stunning but can lead you down the wrong path with incredible confidence.
(<em>&ldquo;You&rsquo;re absolutely right!&rdquo;</em>)
Keep a cool head and focus on value (MVP), not feature creep.</li>
</ul>
<h2 id="back-at-the-office-what-do-we-do">Back at the Office: What Do We Do?</h2>
<p>I concluded with a <em>&ldquo;Todo List&rdquo;</em> for when attendes are back at the office:</p>
<ul>
<li>Don&rsquo;t ask yourself <em>&ldquo;Where can I squeeze in a chatbot?&rdquo;</em>.
Instead, identify the most painful business process (the Critical User Journey).</li>
<li>Experiment small. The goal is to learn.</li>
<li>Measure &amp; Evaluate. It&rsquo;s your users who will tell you if you&rsquo;re right, not the hype.</li>
</ul>
<p>The agent might not buy happiness, but implemented well, it can seriously contribute to it! &#x1f604;</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Gemini Is Cooking Bananas Under Antigravity</title><link>https://glaforge.dev/posts/2025/11/21/gemini-is-cooking-bananas-under-antigravity/</link><pubDate>Fri, 21 Nov 2025 11:14:08 +0100</pubDate><guid>https://glaforge.dev/posts/2025/11/21/gemini-is-cooking-bananas-under-antigravity/</guid><description>&lt;p>What a wild title, isn&amp;rsquo;t it?
It&amp;rsquo;s a catchy one, not generated by AI, to illustrate this crazy week of announcements by Google.
Of course, there are big highlights like &lt;strong>Gemini 3 Pro&lt;/strong>, &lt;strong>Antigravity&lt;/strong>, or &lt;strong>Nano Banana Pro&lt;/strong>, but not only,
and this is the purpose of the article to share with you everything,
including links to all the interesting materials about those news.&lt;/p>
&lt;h2 id="gemini-3-pro">Gemini 3 Pro&lt;/h2>
&lt;p>The community was eagerly anticipating the release of Gemini 3.
Gemini 3 Pro is a state-of-the-art model, with excellent multimodal capabilities,
advanced reasoning, excellent at coding, and other agentic activities.&lt;/p></description><content:encoded>
<![CDATA[<p>What a wild title, isn&rsquo;t it?
It&rsquo;s a catchy one, not generated by AI, to illustrate this crazy week of announcements by Google.
Of course, there are big highlights like <strong>Gemini 3 Pro</strong>, <strong>Antigravity</strong>, or <strong>Nano Banana Pro</strong>, but not only,
and this is the purpose of the article to share with you everything,
including links to all the interesting materials about those news.</p>
<h2 id="gemini-3-pro">Gemini 3 Pro</h2>
<p>The community was eagerly anticipating the release of Gemini 3.
Gemini 3 Pro is a state-of-the-art model, with excellent multimodal capabilities,
advanced reasoning, excellent at coding, and other agentic activities.</p>
<p>You&rsquo;ll see below the results on various benchmarks, which are quite impressive,
and represents a significant leap forward on some of them:
<figure>
  <a href="#img-def7ccaffcd015293ec20ee551ea0d9c">
    <img src="https://storage.googleapis.com/gweb-uniblog-publish-prod/documents/gemini_3_table_final_HLE_Tools_on.gif"
      alt="Gemini 3 Pro results on benchmarks"
       />
  </a>
  <figcaption>Gemini 3 Pro results on benchmarks</figcaption>
</figure>
<div class="lightbox" id="img-def7ccaffcd015293ec20ee551ea0d9c">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://storage.googleapis.com/gweb-uniblog-publish-prod/documents/gemini_3_table_final_HLE_Tools_on.gif"
    alt="Gemini 3 Pro results on benchmarks"
     />
  <div class="lightbox-caption">Gemini 3 Pro results on benchmarks</div>
</div>
</p>
<p>Here are a few pointers with more details:</p>
<ul>
<li><a href="https://blog.google/products/gemini/gemini-3/">A new era of intelligence with Gemini 3</a> with lots of illustrations of what Gemini 3 can do,
and also the mention of Gemini 3 Deep Think, which ranks super high on ARC-AGI-2 or Humanity&rsquo;s Last Exam benchmarks.</li>
<li><a href="https://blog.google/technology/developers/gemini-3-developers/">Start building with Gemini 3</a> mentioning agentic coding, Antigravity (we&rsquo;ll come back to it),
vibe coding, visual, video and spatial reasoning.</li>
<li><a href="https://blog.google/products/gemini/gemini-3-gemini-app/">Gemini 3 brings upgraded smarts and new capabilities to the Gemini app</a>
which discusses what Gemini 3 brings inside the Gemini app with live <em>generative interfaces</em> (generating live UIs on the fly!)</li>
<li><a href="https://blog.google/products/gemini/gemini-3-collection/">All the articles about Gemini 3</a>
which lists all the <em>official</em> blog posts about the launch of Gemini 3, if you want to dive deeper.</li>
</ul>
<h3 id="available-in-many-places">Available In Many Places</h3>
<p>Gemini 3 Pro is available in other places:</p>
<ul>
<li>in <a href="https://developers.googleblog.com/jules-gemini-3/">Jules</a> the asynchronous coding agent,</li>
<li>in <a href="https://firebase.blog/posts/2025/11/gemini-3-firebase-ai-logic">Firebase AI Logic</a>,</li>
<li>in <a href="https://android-developers.googleblog.com/2025/11/gemini-3-is-now-available-for-ai.html">Android Studio</a>,</li>
<li>in <a href="https://blog.jetbrains.com/ai/2025/11/gemini-3-pro-is-now-available-in-jetbrains-ides/">JetBrains IDEs</a> in Junie and the AI chat,</li>
<li>and <a href="https://github.blog/changelog/2025-11-18-gemini-3-pro-is-in-public-preview-for-github-copilot/">inside GitHub Copilot</a>.</li>
</ul>
<h3 id="available-in-gemini-cli">Available In Gemini CLI</h3>
<p>Gemini CLI has also been updated to take advantage of Gemini 3 Pro.</p>
<p>Be sure to read this <a href="https://geminicli.com/docs/get-started/gemini-3/">document</a> which explains how to access Gemini 3 Pro in Gemini CLI,
as it&rsquo;s available to Google AI Ultra users, and paid Gemini and Vertex API key holders,
so if you&rsquo;re not in these categories, you might want to wish the <a href="https://goo.gle/geminicli-waitlist-signup">waitlist</a>
to experience all the fun!</p>
<p>Check out this article as well:</p>
<ul>
<li><a href="https://developers.googleblog.com/en/5-things-to-try-with-gemini-3-pro-in-gemini-cli/">5 things to try with Gemini 3 Pro in Gemini CLI</a>
on how to setup Gemini 3, how to turn visual ideas into working apps, how to generate shell commands from natural language,
or accurate documentation from your codebase, and how it can help you debug issues.</li>
</ul>
<h2 id="antigravity">Antigravity</h2>
<p>Now let&rsquo;s move on to <a href="https://antigravity.google/">Antigravity</a>, a new agentic development platform, based on VS Code.
Of course, you have all the usual functionalities of a text editor, with (smart) code completion, and all.
However the interesting aspect of Antigravity is that the main window is actually not the IDE,
but the central place where things happen is the <em>agent manager</em>, where you&rsquo;ll launch your requests.
They will be interpreted and will trigger the creation of a plan, with various steps.
You&rsquo;ll be able to comment and review the plan, for further adjustments.
Then Gemini 3 will handle the implementation of those tasks.
And Antigravity will produce various other artifacts along the way, like the task lists, the walkthroughs, screenshots and even browser recordings.</p>
<p><figure>
  <a href="#img-e47a699e68d0d2d300827bb8ac2765da">
    <img src="https://antigravity.google/assets/image/product/manager.png"
      alt="Antigravity agent manager screenshot"
       />
  </a>
  <figcaption>Antigravity agent manager screenshot</figcaption>
</figure>
<div class="lightbox" id="img-e47a699e68d0d2d300827bb8ac2765da">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://antigravity.google/assets/image/product/manager.png"
    alt="Antigravity agent manager screenshot"
     />
  <div class="lightbox-caption">Antigravity agent manager screenshot</div>
</div>
</p>
<p>It&rsquo;s also possible to use Claude Sonnet and GPT-OSS, so this product is not limited to just Gemini 3, however good it may be.</p>
<p>What I find impressive is the nice integration with the browser, to inspect and see how the implementation looks like,
and further loop back to continue improving it or fixing it if it&rsquo;s not what the user asked.</p>
<p>I haven&rsquo;t covered Nano Banana Pro yet, but with that image generation and editing model integrated in Antigravity,
you&rsquo;re able to create designs, update them visually with manual squiggles and such, and have Antigravity implement that design for you!</p>
<p>Articles to dive more:</p>
<ul>
<li><a href="https://antigravity.google/blog/introducing-google-antigravity">Introducing Google Antigravity, a New Era in AI-Assisted Software Development</a></li>
<li><a href="https://developers.googleblog.com/build-with-google-antigravity-our-new-agentic-development-platform/">Build with Google Antigravity, our new agentic development platform</a>,
the blog post announcing Antigravity</li>
<li><a href="https://antigravity.google/docs/get-started">Getting Started</a> to know how to get your environment ready and configured</li>
<li><a href="https://antigravity.google/blog/nano-banana-pro-in-google-antigravity">Nano Banana Pro in Google Antigravity</a>
dives into the integration with Nano Banana to generate UI mockups, incrementally, and get your design implemented.</li>
<li><a href="https://www.youtube.com/@googleantigravity">Antigravity YouTube Channel</a> with plenty of small videos, including a longer one to
<a href="https://www.youtube.com/watch?v=nTOVIGsqCuY">learn the basics</a>, and another short one demonstrating the
<a href="https://www.youtube.com/watch?v=FB6HO7CZHWw">Nano Banana integration</a>.</li>
<li><a href="https://medium.com/google-cloud/tutorial-getting-started-with-google-antigravity-b5cc74c103c2">Tutorial : Getting Started with Google Antigravity</a>
by my awesome colleague Romin Irani who wrote a very detailed step-by-step tutorial to get started easily,
and who also created a <a href="https://codelabs.developers.google.com/getting-started-google-antigravity?hl=en#0">codelab</a>
with precise instructions to go through.</li>
</ul>
<h2 id="nano-banana-pro">Nano Banana Pro</h2>
<p>I wrote about <strong>Nano Banana</strong> in previous articles showing how to
<a href="https://glaforge.dev/posts/2025/10/24/javelit-to-create-quick-interactive-app-frontends-in-java/">call it from a Javelit frontend</a>,
how to create <a href="https://glaforge.dev/posts/2025/09/22/creative-ai-agents-with-adk-and-nano-banana/">ADK agents with Nano Banana</a>,
and simply how to <a href="https://glaforge.dev/posts/2025/09/09/calling-nano-banana-from-java/">call Nano Banana from Java</a>.</p>
<p>I was already super impressed with its capabilities in terms of image edition.
However if I wanted the best quality, I would usually start with an Imagen generation,
then I&rsquo;d iterate with Nano Banana for editing.
But now, Nano Banana Pro is another level above both Imagen 4 Ultra and the original Nano Banana,
in terms of prompt adherence, understanding of user intent, creativity, and quality of generation.</p>
<p>When you use it, you&rsquo;ll notice how great it is at text, even lots of text!
It made huge leaps in terms of typography.
And what&rsquo;s crazy, with the fact it&rsquo;s based on Gemini 3 Pro, is that it&rsquo;s able to understand articles or videos,
and generate detailed and precise infographics about them!
It&rsquo;s connected to Google Search, and it can research, for example, the weather in Paris, and create a diagram with live data!
I&rsquo;ll certainly come back to that topic in forthcoming articles.</p>
<p>For example, here&rsquo;s some infographics that Nano Banana Pro created to summarize this article:
<figure>
  <a href="#img-98ad0e4d4293c4a1134aae9db1e9fe66">
    <img src="/img/nano-banana/nb2-article-infographics.jpg"
      alt="Illustration of this article via Nano Banana Pro"
       />
  </a>
  <figcaption>Illustration of this article via Nano Banana Pro</figcaption>
</figure>
<div class="lightbox" id="img-98ad0e4d4293c4a1134aae9db1e9fe66">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/nano-banana/nb2-article-infographics.jpg"
    alt="Illustration of this article via Nano Banana Pro"
     />
  <div class="lightbox-caption">Illustration of this article via Nano Banana Pro</div>
</div>
</p>
<p>You can mix <em>ingredients</em> together like different characters (with character consistency),
use them for some kind of <em>transfer learning</em> to follow a certain style.</p>
<p>It has a high level of understanding of scenes and can easily change the lighting, the angle view.</p>
<p>You can generate images up to 4K! And have a wide range of aspect ratios to choose from.</p>
<p>Pay attention to the pricing, however, as it&rsquo;s more expensive than Nano Banana.
So for small edits, maybe you&rsquo;ll stick with Nano Banana, but when you want the most complex design and quality, choose Nano Banana Pro.</p>
<p>Some links to dive deeper:</p>
<ul>
<li><a href="https://blog.google/technology/ai/nano-banana-pro/">Introducing Nano Banana Pro</a>, the blog post.</li>
<li><a href="https://blog.google/technology/developers/gemini-3-pro-image-developers/">Build with Nano Banana Pro, our Gemini 3 Pro Image model</a>,
another official blog post on the launch.</li>
<li><a href="https://deepmind.google/models/gemini-image/pro/">https://deepmind.google/models/gemini-image/pro/</a>,
DeepMind&rsquo;s product page showing some impressive examples of usage of the model.</li>
<li><a href="https://storage.googleapis.com/deepmind-media/Model-Cards/Gemini-3-Pro-Image-Model-Card.pdf">Gemini 3 Pro Image Model Card</a>
for all the technical details of the model.</li>
<li><a href="https://medium.com/google-cloud/testing-gemini-3-pro-image-f585236ae411">Testing Gemini 3 Pro Image Model</a>
written by my awesome colleague Laurent Picard who demonstrates character consistency and the high quality of generated images.</li>
<li><a href="https://blog.google/products/gemini/prompting-tips-nano-banana-pro/">7 tips to get the most out of Nano Banana Pro</a>
to get the best out of the model.</li>
</ul>
<h2 id="towards-live-and-richer-conversational-uis">Towards Live And Richer Conversational UIs</h2>
<p>I wanted to finish this overview of the announcements of the week with something that you might not have heard of,
but which I think is interesting for the future of generative AI and conversational interfaces.</p>
<p>In my talks, at meetups, in conversations with developers, I often explain that imposing chatbots everywhere
is not the best use and ideal integration of AI in their applications,
and that more transparent and seamless generative AI integrations are preferred for ensuring their success with users and customers.</p>
<p>I think the following two projects are helping towards a smoother integration of generative AI:</p>
<p><figure>
  <a href="#img-3f63f8b2167b73bba05691587bee30e4">
    <img src="https://miro.medium.com/v2/resize:fit:4800/format:webp/1*yZgCoXy4aivZfEjgTJMOrA.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-3f63f8b2167b73bba05691587bee30e4">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://miro.medium.com/v2/resize:fit:4800/format:webp/1*yZgCoXy4aivZfEjgTJMOrA.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<ul>
<li><a href="https://blog.flutter.dev/rich-and-dynamic-user-interfaces-with-flutter-and-generative-ui-178405af2455">Rich and dynamic user interfaces with Flutter and generative UI</a></li>
</ul>
<p>The GenUI SDK for <a href="https://flutter.dev/">Flutter</a> allows developers to create dynamic, personalized user interfaces using LLMs,
transforming text-based conversations into rich, interactive experiences.
It acts as an orchestration layer, sending user prompts and available widgets to an AI agent
which generates content and a suitable UI description.
The SDK then deserializes and dynamically renders this UI into interactive Flutter widgets,
with user interactions triggering subsequent updates.
This system relies on the (upcoming) <a href="https://a2ui.org/">A2UI protocol</a>.</p>
<ul>
<li><a href="https://research.google/blog/generative-ui-a-rich-custom-visual-interactive-user-experience-for-any-prompt/">Generative UI: A rich, custom, visual interactive user experience for any prompt</a></li>
</ul>
<p>Generative UI enables AI models to create custom, interactive user experiences, like web pages and tools, directly from any prompt.
This dynamic capability is rolling out in the Gemini app and Google Search&rsquo;s AI Mode,
leveraging Gemini 3 Pro with tool access and detailed instructions.
Users prefer these outputs over standard AI text, though human-expert designs remain slightly favored.
Despite current challenges with speed and occasional inaccuracies, Generative UI signifies a major step toward fully AI-generated and adaptive interfaces.</p>
<h2 id="now-your-turn-to-have-fun">Now, Your Turn To Have Fun!</h2>
<p>With all those announcements, and key pointers to learn more about them,
I hope you&rsquo;re ready to build exciting new things with Gemini 3 Pro, Antigravity, Nano Banana Pro, and more!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Semantic Document Similarity: Finding related articles with vector embedding models</title><link>https://glaforge.dev/posts/2025/11/12/finding-related-articles-with-vector-embedding-models/</link><pubDate>Wed, 12 Nov 2025 08:52:42 +0100</pubDate><guid>https://glaforge.dev/posts/2025/11/12/finding-related-articles-with-vector-embedding-models/</guid><description>&lt;p>When you enjoyed reading an article on a blog, you might be interested in other, similar articles.
As a blog author, you want to surface that relevant content to your readers to keep them engaged.
For a long time, I&amp;rsquo;ve wanted to add a &lt;em>&amp;ldquo;Similar articles&amp;rdquo;&lt;/em> section to my posts, but I never quite found a simple and effective way to do it.
Hugo (the static stite generator I&amp;rsquo;m using) has a &lt;a href="https://gohugo.io/content-management/related-content/">related content&lt;/a> concept, but it wasn&amp;rsquo;t really what I was after.&lt;/p></description><content:encoded>
<![CDATA[<p>When you enjoyed reading an article on a blog, you might be interested in other, similar articles.
As a blog author, you want to surface that relevant content to your readers to keep them engaged.
For a long time, I&rsquo;ve wanted to add a <em>&ldquo;Similar articles&rdquo;</em> section to my posts, but I never quite found a simple and effective way to do it.
Hugo (the static stite generator I&rsquo;m using) has a <a href="https://gohugo.io/content-management/related-content/">related content</a> concept, but it wasn&rsquo;t really what I was after.</p>
<p>But with the power of modern generative AI models (in particular, embedding models),
I&rsquo;ve finally implemented a system that automatically finds and displays related content.
In this post, I&rsquo;ll walk you through how I did it.</p>
<h2 id="the-core-idea-from-words-to-numbers">The Core Idea: From Words to Numbers</h2>
<p>The fundamental challenge is to determine how <em>&ldquo;similar&rdquo;</em> two articles are.
Humans can do this intuitively by reading them, but how can a computer do it? The answer lies in a technique called <strong>vector embeddings</strong>.</p>
<p>The idea is to convert a piece of text into a list of numbers, called a vector.
This vector represents the text&rsquo;s semantic meaning.
Texts with similar meanings will have vectors that are &ldquo;close&rdquo; to each other in a multi-dimensional space.
So, the process looks like this:</p>
<ol>
<li><strong>Summarize:</strong> For each article, create a concise summary that captures its essence.</li>
<li><strong>Embed:</strong> Convert each summary into a vector embedding.</li>
<li><strong>Compare:</strong> Calculate the &ldquo;distance&rdquo; between every pair of vectors.</li>
<li><strong>Display:</strong> For each article, find the ones with the closest vectors and display them.</li>
</ol>
<p>Let&rsquo;s dive into each step.</p>
<h3 id="step-1-summarizing-the-content">Step 1: Summarizing the Content</h3>
<p>My blog posts can be quite long and cover various topics.
To get a clean signal for comparison, I first decided to summarize each article.
This helps to distill the core message and remove noise.</p>
<p>This approach was also necessary because most embedding models have limitations on the size of the input text they can accept.
Many of my articles were too long for the embedding model&rsquo;s input, so creating detailed summaries that are as close as possible to the original content ensures that the resulting embedding vector accurately represents the article&rsquo;s meaning.</p>
<p>For this task, I turned to Google&rsquo;s <strong>Gemini</strong> model, specifically <code>gemini-2.5-flash</code>, which is fast and effective.
With the help of <a href="https://geminicli.com/">Gemini CLI</a>,
I wrote a simple Node.js <a href="https://github.com/glaforge/glaforge.github.io/blob/main/summarize-and-embed.js">script</a>
that iterates through all my Markdown files, extracts the content, and sends it to the Gemini API with a straightforward prompt:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> prompt <span style="color:#666">=</span> <span style="color:#4070a0">`Please provide a long, detailed, and factual summary
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">of the following article. The summary should capture the main points,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">key arguments, and any important conclusions. It should be
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">comprehensive enough to give a good understanding of the article&#39;s
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">content.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">Article:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0"></span><span style="color:#70a0d0">${</span>text<span style="color:#70a0d0">}</span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">Summary:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">`</span>
</span></span></code></pre></div><p>To call Gemini, you need to import the <code>@google/genai module</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> {GoogleGenAI} from <span style="color:#4070a0">&#39;@google/genai&#39;</span>;
</span></span></code></pre></div><p>Pass an API key (that you can obtain from <a href="https://aistudio.google.com/api-keys">Google AI Studio</a>):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> genAI <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> GoogleGenAI({apiKey<span style="color:#666">:</span> API_KEY});
</span></span></code></pre></div><p>Then call the model to create the summary:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> result <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">await</span> genAI.models.generateContent({
</span></span><span style="display:flex;"><span>    model<span style="color:#666">:</span> <span style="color:#4070a0">&#34;gemini-2.5-flash&#34;</span>,
</span></span><span style="display:flex;"><span>    contents<span style="color:#666">:</span> prompt,
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> summary <span style="color:#666">=</span> result<span style="color:#666">?</span>.candidates<span style="color:#666">?</span>.[<span style="color:#40a070">0</span>]<span style="color:#666">?</span>.content<span style="color:#666">?</span>.parts<span style="color:#666">?</span>.[<span style="color:#40a070">0</span>]<span style="color:#666">?</span>.text;
</span></span></code></pre></div><p>To avoid re-generating summaries every time I run the script, I added a simple caching mechanism.
The first time the script runs, it generates a summary and saves it to a text file in a <code>summaries/</code> directory, mirroring the structure of my <code>content/posts/</code> directory. On subsequent runs, it just reads the summary from the cache.</p>
<h3 id="step-2-creating-vector-embeddings">Step 2: Creating Vector Embeddings</h3>
<p>Once I have a summary, the next step is to convert it into a vector. This is where embedding models come in.
I used another of Google&rsquo;s models, <code>gemini-embedding-001</code>, which is designed for this exact purpose.</p>
<p>The model takes a piece of text and returns a vector.
You can even configure the size of this vector.
I chose a dimensionality of 256, which provides a good balance between detail and performance (to speed up vector similarity calculations).</p>
<p>I also specified the <code>task_type</code> as <code>SEMANTIC_SIMILARITY</code>, which optimizes the embeddings for this kind of comparison task.
You can check the other <a href="https://ai.google.dev/gemini-api/docs/embeddings#supported-task-types">task types</a>, for classification, clustering, Q&amp;A, etc.</p>
<p>Again, this is fairly short to write in JavaScript:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> result <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">await</span> genAI.models.embedContent({
</span></span><span style="display:flex;"><span>    model<span style="color:#666">:</span> <span style="color:#4070a0">&#34;gemini-embedding-001&#34;</span>,
</span></span><span style="display:flex;"><span>    contents<span style="color:#666">:</span> texts,
</span></span><span style="display:flex;"><span>    task_type<span style="color:#666">:</span> <span style="color:#4070a0">&#34;SEMANTIC_SIMILARITY&#34;</span>,
</span></span><span style="display:flex;"><span>    output_dimensionality<span style="color:#666">:</span> <span style="color:#40a070">256</span>,
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> embeddings <span style="color:#666">=</span> result<span style="color:#666">?</span>.embeddings;
</span></span></code></pre></div><p>My script collects all the summaries and sends them to the embedding model in batches of 100 (the maximum allowed by the API).
This is much more efficient than sending them one by one.</p>
<h3 id="step-3-calculating-similarity">Step 3: Calculating Similarity</h3>
<p>Now that every article&rsquo;s summary is represented by a 256-dimensional vector, how do we measure the &ldquo;distance&rdquo; between them?
The most common method for this is <strong>cosine similarity</strong>.</p>
<p>Imagine each vector as an arrow pointing in a certain direction in a 256-dimensional space.
The <a href="https://en.wikipedia.org/wiki/Cosine_similarity">cosine similarity</a> measures the angle between two of these arrows.</p>
<ul>
<li>If the arrows point in the same direction (a small angle), the texts are very similar, and the cosine similarity is close to 1.</li>
<li>If the arrows are perpendicular, the texts are unrelated, and the similarity is 0.</li>
<li>If they point in opposite directions, they are dissimilar, and the similarity is -1.</li>
</ul>
<p>The calculation itself is quite simple.
When the vectors are normalized
(which I do, as the vectors are not always normalized depending on the output dimensionality you&rsquo;ve chosen),
the cosine similarity is just their <a href="https://en.wikipedia.org/wiki/Dot_product">dot product</a>.</p>
<h3 id="step-4-putting-it-all-together-in-a-script">Step 4: Putting It All Together in a Script</h3>
<p>The Node.js script that orchestrates this whole process is available on GitHub: <a href="https://github.com/glaforge/glaforge.github.io/blob/main/summarize-and-embed.js">summarize-and-embed.js</a>.</p>
<p>Here&rsquo;s how it works:</p>
<ol>
<li>It finds all my blog posts.</li>
<li>It loops through them, either loading the summary from the cache or generating a new one (with a one-second delay between API calls to avoid rate limiting).</li>
<li>It generates embeddings for all summaries in batches.</li>
<li>It then iterates through every article and calculates the cosine similarity with every other article.</li>
<li>For each article, it sorts the others by their similarity score in descending order. I also filter out any articles with a similarity score below 0.75 to ensure the recommendations are high-quality.</li>
<li>Finally, it takes the top 3 most similar articles and updates the frontmatter of the original Markdown file, adding a <code>similar</code> array with the paths to the related posts.</li>
</ol>
<h3 id="step-5-displaying-the-similar-articles-in-hugo">Step 5: Displaying the Similar Articles in Hugo</h3>
<p>With the <code>similar</code> array in my frontmatter, the final step was to display the links in my blog&rsquo;s theme.
I&rsquo;m using the Hugo static site generator, and this was surprisingly easy.
I edited the partial template responsible for rendering a single post to include this snippet:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>{{ with .Params.similar }}
</span></span><span style="display:flex;"><span>&lt;<span style="color:#062873;font-weight:bold">div</span> <span style="color:#4070a0">class</span><span style="color:#666">=</span><span style="color:#4070a0">&#34;similar-articles&#34;</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#062873;font-weight:bold">h3</span>&gt;Similar articles&lt;/<span style="color:#062873;font-weight:bold">h3</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#062873;font-weight:bold">ul</span>&gt;
</span></span><span style="display:flex;"><span>    {{ range . }}
</span></span><span style="display:flex;"><span>        {{ with site.GetPage . }}
</span></span><span style="display:flex;"><span>            &lt;<span style="color:#062873;font-weight:bold">li</span>&gt;&lt;<span style="color:#062873;font-weight:bold">a</span> <span style="color:#4070a0">href</span><span style="color:#666">=</span><span style="color:#4070a0">&#34;{{ .RelPermalink }}&#34;</span>&gt;{{ .Title }}&lt;/<span style="color:#062873;font-weight:bold">a</span>&gt;&lt;/<span style="color:#062873;font-weight:bold">li</span>&gt;
</span></span><span style="display:flex;"><span>        {{ end }}
</span></span><span style="display:flex;"><span>    {{ end }}
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#062873;font-weight:bold">ul</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#062873;font-weight:bold">div</span>&gt;
</span></span><span style="display:flex;"><span>{{ end }}
</span></span></code></pre></div><p>This code checks if the <code>similar</code> parameter exists. If it does, it loops through the paths.
For each path, it uses Hugo&rsquo;s <code>site.GetPage</code> function to fetch the full page object, from which I can get the <code>.Title</code> and <code>.RelPermalink</code>.
And with a little bit of CSS, it looks like a nice, integrated part of my blog.</p>
<h2 id="what-does-it-look-like">What Does It Look Like?</h2>
<p>Let&rsquo;s have a look at a few examples (you&rsquo;ll see the recommended articles at the bottom of each post).</p>
<p>For my article on the <a href="https://glaforge.dev/posts/2025/11/03/driving-a-web-browser-with-gemini-computer-use-model-in-java/">Gemini Computer Use model</a>,
the vector similarity suggested 3 articles related to Gemini as well:</p>
<ul>
<li><a href="https://glaforge.dev/posts/2023/12/13/get-started-with-gemini-in-java/">Get Started with Gemini in Java</a></li>
<li><a href="https://glaforge.dev/posts/2024/09/05/new-gemini-model-in-langchain4j/">New Gemini model in LangChain4j</a></li>
<li><a href="https://glaforge.dev/posts/2023/12/22/gemini-function-calling/">Gemini Function Calling</a></li>
</ul>
<p>On the article about <a href="https://glaforge.dev/posts/2025/09/22/creative-ai-agents-with-adk-and-nano-banana/">using the Nano Banana image model within an ADK agent</a>,
the algorithm refered to those 3 articles on getting started with ADK, how to call Nano Banana from Java, or how to get started with Gemini:</p>
<ul>
<li><a href="https://glaforge.dev/posts/2025/09/09/calling-nano-banana-from-java/">Generating and editing images with Nano Banana in Java</a></li>
<li><a href="https://glaforge.dev/posts/2025/05/20/writing-java-ai-agents-with-adk-for-java-getting-started/">Write AI agents in Java — Agent Development Kit getting started guide</a></li>
<li><a href="https://glaforge.dev/posts/2023/12/13/get-started-with-gemini-in-java/">Get Started with Gemini in Java</a></li>
</ul>
<p>For some quite unique articles, sometimes it would return even just one or zero result at all,
but that&rsquo;s expected, because if there are no articles that are similar,
I don&rsquo;t want my readers to get suggestions about totally unrelated material.</p>
<h2 id="considerations-and-other-approaches">Considerations and Other Approaches</h2>
<p>I&rsquo;m happy with the <em>summarize &ndash;&gt; embed approach</em> as it is simple and gives me pretty good recommendations.
However, I considered some alternatives.</p>
<h3 id="leveraging-tags">Leveraging Tags</h3>
<p>First of all, I&rsquo;m using <em>tags</em> on my blog.
So I considered leveraging them, as I strive for consistency in applying them to my articles.
For instance, I could have narrowed the search space to only articles sharing at least one common tag.
Alternatively, a heuristic could have been devised to influence the ranking, favoring articles with a higher number of shared tags.</p>
<h3 id="averaging-vectors-mean-pooling">Averaging Vectors (Mean Pooling)</h3>
<p>I went with the summarization route because of the limited input size of the embedding model.
But <strong>summarization is a lossy process</strong>, as you lose some precision along the way.
Through some experiments, I noticed that summarization could sometimes result in a similarity score that was 0.1 lower than a direct embedding of the full text.
I have a threshold of similarity of &gt;0.75, so 0.1 can make a difference for article selection.</p>
<p>If you chunk your article into smaller passages (according to the maximum number of characters or tokens your embedding model can ingest), you&rsquo;ll end up with multiple chunks and their respective vector embeddings.
One approach is to calculate the average of these vectors, assuming this &lsquo;mean pooled&rsquo; vector isn&rsquo;t too far from the embedding of the entire text.
I didn&rsquo;t extensively explore this, but my initial impressions suggested it sometimes performed worse than the summarization and embedding method.
This wasn&rsquo;t a scientific study, just my gut feeling, and the finer details of research papers on mean pooling escaped me at the time! Averaging vectors is often mentioned in articles as <em>mean pooling</em>.</p>
<h3 id="passage-to-passage-comparisons">Passage to Passage Comparisons</h3>
<p>A higher fidelity approach would have been to do a passage-to-passage comparison.
If you&rsquo;re familiar with <strong>RAG</strong> (<a href="https://glaforge.dev/tags/retrieval-augmented-generation/">Retrieval Augmented Generation</a>), you know about the chunking phase we&rsquo;ve just mentioned.
In RAG, you compare the query&rsquo;s vector with vectors of document passages.
This passage-to-passage comparison approach could also be applied to compare a full article with other full articles.
You could then devise a function to aggregate these passage-level similarities into an overall document score, favoring documents with more highly similar passages.
This could be a higher fidelity comparison, but I didn&rsquo;t explore this idea as the number of embedding requests and matrix comparisons is significantly higher (and thus more expensive and time consuming).</p>
<h3 id="a-mix-with-reciprocal-rank-fusion">A Mix! With Reciprocal Rank Fusion</h3>
<p>Last but not least, you could even combine those approaches together: summarize/embed, tag set comparison, passage-to-passage chunking/embedding and comparison.
Each approach yields a ranking of similar articles, but their scores aren&rsquo;t necessarily on the same scale, as they&rsquo;re comparing different aspects of similarity.
In such situations, you can use methods like <a href="https://medium.com/@devalshah1619/mathematical-intuition-behind-reciprocal-rank-fusion-rrf-explained-in-2-mins-002df0cc5e2a">Reciprocal Rank Fusion</a> (RRF)
to combine different rankings together.
RRF is often used in hybrid search scenarios, where you want to combine different searches together, like a classical keyword-based search, and a semantic search.</p>
<p>Ultimately, since the simple summarization-and-embed solution provided good results, I decided against further complicating the system.
Sometimes, being pragmatic and choosing a &lsquo;good enough&rsquo; solution is more effective than pursuing the absolute best.
However, I wanted to share this thought process and highlight that various other solutions exist.</p>
<h2 id="conclusion">Conclusion</h2>
<p>And there you have it!
It might seem like a lot of steps, but the overall process is quite logical.
By leveraging the power of <strong>summarization</strong> and <strong>embedding models</strong>, I was able to build a powerful <strong>related articles feature</strong>.
For now, I&rsquo;m still running the script manually, but later on, I&rsquo;ll see how I can integrate it in my GitHub Actions workflow.</p>
<p>It&rsquo;s a great example of how generative AI can be used to enhance existing applications in practical and useful ways.
I&rsquo;m really happy with the results, and I hope my readers will find the new recommendations useful for discovering more content they&rsquo;re interested in.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Driving a web browser with Gemini's Computer Use model in Java</title><link>https://glaforge.dev/posts/2025/11/03/driving-a-web-browser-with-gemini-computer-use-model-in-java/</link><pubDate>Sun, 02 Nov 2025 00:00:00 +0000</pubDate><guid>https://glaforge.dev/posts/2025/11/03/driving-a-web-browser-with-gemini-computer-use-model-in-java/</guid><description>&lt;p>In this article, I&amp;rsquo;ll guide you through the process of programmatically interacting with a web browser
using the new &lt;a href="https://ai.google.dev/gemini-api/docs/computer-use">Computer Use model&lt;/a> in Gemini 2.5 Pro.
We&amp;rsquo;ll accomplish this in Java &amp;#x2615; leveraging Microsoft&amp;rsquo;s powerful &lt;a href="https://playwright.dev/java/">Playwright Java SDK&lt;/a> to handle the browser automation.&lt;/p>
&lt;h2 id="the-new-computer-use-model">The New Computer Use Model&lt;/h2>
&lt;p>Unveiled in this &lt;a href="https://blog.google/technology/google-deepmind/gemini-computer-use-model/">announcement article&lt;/a>
and made available in public preview last month, via the Gemini API
on &lt;a href="https://aistudio.google.com/prompts/new_chat">Google AI Studio&lt;/a> and
&lt;a href="https://docs.cloud.google.com/vertex-ai/generative-ai/docs/computer-use">Vertex AI&lt;/a>,
Gemini 2.5 Pro introduces a pretty powerful &lt;a href="https://ai.google.dev/gemini-api/docs/computer-use">&amp;ldquo;Computer Use&amp;rdquo;&lt;/a> feature.&lt;/p></description><content:encoded>
<![CDATA[<p>In this article, I&rsquo;ll guide you through the process of programmatically interacting with a web browser
using the new <a href="https://ai.google.dev/gemini-api/docs/computer-use">Computer Use model</a> in Gemini 2.5 Pro.
We&rsquo;ll accomplish this in Java &#x2615; leveraging Microsoft&rsquo;s powerful <a href="https://playwright.dev/java/">Playwright Java SDK</a> to handle the browser automation.</p>
<h2 id="the-new-computer-use-model">The New Computer Use Model</h2>
<p>Unveiled in this <a href="https://blog.google/technology/google-deepmind/gemini-computer-use-model/">announcement article</a>
and made available in public preview last month, via the Gemini API
on <a href="https://aistudio.google.com/prompts/new_chat">Google AI Studio</a> and
<a href="https://docs.cloud.google.com/vertex-ai/generative-ai/docs/computer-use">Vertex AI</a>,
Gemini 2.5 Pro introduces a pretty powerful <a href="https://ai.google.dev/gemini-api/docs/computer-use">&ldquo;Computer Use&rdquo;</a> feature.</p>
<p>This allows the model to understand and interact with a computer screen much like a human would.
It&rsquo;s a multimodal model that takes a screenshot of a web page as input and returns a sequence of actions to perform,
such as clicking buttons, filling out text fields, and navigating through pages,
until it reaches a certain goal set by the user.</p>
<p>The general flow is illustrated in the following diagram:</p>
<p><figure>
  <a href="#img-a5972b85b2298fe92ff466986e8dfa4a">
    <img src="/img/gemini/browser-use/computer-use-flow.png"
      alt="Gemini Computer Use flow"
       />
  </a>
  <figcaption>Gemini Computer Use flow</figcaption>
</figure>
<div class="lightbox" id="img-a5972b85b2298fe92ff466986e8dfa4a">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/browser-use/computer-use-flow.png"
    alt="Gemini Computer Use flow"
     />
  <div class="lightbox-caption">Gemini Computer Use flow</div>
</div>
</p>
<p>Let&rsquo;s get the project set up, and then we&rsquo;ll see further down how this flow works, and how to implement it, using Java and Playwright.</p>
<h2 id="project-setup">Project Setup</h2>
<p>For this tutorial, I&rsquo;m using a straightforward Java project built with <a href="https://maven.apache.org/">Maven</a>.
We&rsquo;ll need two main dependencies: one for the <a href="https://central.sonatype.com/artifact/com.google.genai/google-genai">Gemini API</a>
and another for <a href="https://central.sonatype.com/artifact/com.microsoft.playwright/playwright">Playwright</a>.</p>
<p>Here&rsquo;s the relevant section of my <code>pom.xml</code> file:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependencies&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>com.google.genai<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>google-genai<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>1.24.0<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>com.microsoft.playwright<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>playwright<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>1.56.0<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependencies&gt;</span>
</span></span></code></pre></div><h2 id="getting-started-with-playwright">Getting Started with Playwright</h2>
<p>Now, let&rsquo;s dive into the code.
<a href="https://playwright.dev/java/">Playwright</a> is a library from Microsoft for automating browser actions,
and it&rsquo;s available for several languages, including Java.</p>
<p>Here’s a basic example of how to launch a browser and navigate to a page:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.microsoft.playwright.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">BrowserAutomation</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">main</span>(String<span style="color:#666">[]</span><span style="color:#bbb"> </span>args)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(Playwright<span style="color:#bbb"> </span>playwright<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Playwright.<span style="color:#4070a0">create</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>Browser<span style="color:#bbb"> </span>browser<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>playwright.<span style="color:#4070a0">chromium</span>().<span style="color:#4070a0">launch</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>BrowserType.<span style="color:#4070a0">LaunchOptions</span>().<span style="color:#4070a0">setHeadless</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>Page<span style="color:#bbb"> </span>page<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>browser.<span style="color:#4070a0">newPage</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>page.<span style="color:#4070a0">navigate</span>(<span style="color:#4070a0">&#34;https://www.google.com&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#60a0b0;font-style:italic">// We&#39;ll add the Gemini integration here.</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>browser.<span style="color:#4070a0">close</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>In this snippet, I&rsquo;m using the <a href="https://www.chromium.org/chromium-projects/">Chromium</a> browser that ships with Playwright.
But other browsers like <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> are also available.</p>
<p>I&rsquo;m launching it in <em>&ldquo;headless&rdquo;</em> mode, meaning no browser window will be visible.
This is ideal for automated tasks where visual inspection isn&rsquo;t necessary.
For debugging, however, you can set <code>setHeadless(false)</code> to watch the automation in real-time.</p>
<h2 id="integrating-with-gemini">Integrating with Gemini</h2>
<p>First, you&rsquo;ll need a Gemini API key, which you can obtain from <a href="https://aistudio.google.com/app/apikey">Google AI Studio</a>.
Once you have your key, it&rsquo;s best practice to set it as an environment variable named <code>GEMINI_API_KEY</code>.
We&rsquo;ll use a try with resources block to create (and close automatically) the client to access Gemini:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(Client<span style="color:#bbb"> </span>client<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Client.<span style="color:#4070a0">Builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>With the browser automation foundation in place, we can now integrate Gemini to steer it.
The process works as a loop: we send a prompt with a goal to reach to the model,
the model returns a series of actions, our code executes them using Playwright,
we share the screenshot of the current web page after each action,
and we repeat the cycle until the task is complete.</p>
<h3 id="the-agent-loop">The Agent Loop</h3>
<p>The Gemini documentation refers to this cyclical process as an <em>&ldquo;agent loop&rdquo;</em>:</p>
<ol>
<li><strong>Send Request</strong>: The loop begins by sending the user&rsquo;s prompt, the latest UI screenshot, and the enabled <code>computer_use</code> tool to the model.</li>
<li><strong>Receive Response</strong>: The model analyzes the inputs and returns a <code>function_call</code> suggesting a specific UI action.</li>
<li><strong>Execute Action</strong>: Our Java code parses this <code>function_call</code> and translates it into a Playwright command to be executed in the browser.</li>
<li><strong>Capture New State</strong>: After the action is performed, we capture a new screenshot and send it back to the model along with a <code>function_response</code>, starting the loop over.</li>
</ol>
<p>This process continues until the model determines the initial goal has been met.</p>
<h3 id="code-walkthrough">Code Walkthrough</h3>
<p>Let&rsquo;s examine the <code>BrowserUse.java</code> file to see how this loop is implemented.
(You&rsquo;ll find the full source code at the bottom of the page.)</p>
<p>First, we initialize Playwright and the Gemini client (as explained above),
setting a specific viewport size which is crucial for the coordinate scaling we&rsquo;ll discuss shortly:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(Playwright<span style="color:#bbb"> </span>playwright<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Playwright.<span style="color:#4070a0">create</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>BrowserType<span style="color:#bbb"> </span>chromium<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>playwright.<span style="color:#4070a0">chromium</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Browser<span style="color:#bbb"> </span>browser<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>chromium.<span style="color:#4070a0">launch</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>BrowserType.<span style="color:#4070a0">LaunchOptions</span>().<span style="color:#4070a0">setHeadless</span>(<span style="color:#007020;font-weight:bold">true</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>WIDTH<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>1000;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>HEIGHT<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>1000;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>BrowserContext<span style="color:#bbb"> </span>context<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>browser.<span style="color:#4070a0">newContext</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Browser.<span style="color:#4070a0">NewContextOptions</span>().<span style="color:#4070a0">setViewportSize</span>(WIDTH,<span style="color:#bbb"> </span>HEIGHT));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Page<span style="color:#bbb"> </span>page<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>context.<span style="color:#4070a0">newPage</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(Client<span style="color:#bbb"> </span>client<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Client.<span style="color:#4070a0">Builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// The agent loop will go here.</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Next, we kick off the conversation with the model by sending our initial prompt.
This defines the overall goal for the agent.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>List<span style="color:#666">&lt;</span>Content<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>history<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ArrayList<span style="color:#666">&lt;&gt;</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Content<span style="color:#bbb"> </span>initialContent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Content.<span style="color:#4070a0">fromParts</span>(Part.<span style="color:#4070a0">fromText</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Find the tallest Stitch plushie under €100 on Amazon.fr
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    and then provide the link to that item.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>history.<span style="color:#4070a0">add</span>(initialContent);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>GenerateContentResponse<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">models</span>.<span style="color:#4070a0">generateContent</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;gemini-2.5-computer-use-preview-10-2025&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>initialContent,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>GenerateContentConfig.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">tools</span>(Tool.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">computerUse</span>(ComputerUse.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">environment</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>Environment.<span style="color:#4070a0">Known</span>.<span style="color:#4070a0">ENVIRONMENT_BROWSER</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>());<span style="color:#bbb">
</span></span></span></code></pre></div><p>Here, we&rsquo;re using the <code>gemini-2.5-computer-use-preview-10-2025</code> model and explicitly enabling the <code>computer_use</code> tool.</p>
<p>The core of the application is a <code>while</code> loop that continues as long as the model returns function calls for us to execute:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">while</span><span style="color:#bbb"> </span>(response.<span style="color:#4070a0">functionCalls</span>()<span style="color:#bbb"> </span><span style="color:#666">!=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span><span style="color:#666">&amp;&amp;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#666">!</span>response.<span style="color:#4070a0">functionCalls</span>().<span style="color:#4070a0">isEmpty</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>We iterate over the function calls returned by the model and use a <code>switch</code> statement to execute the corresponding Playwright action.
After each interaction, we save a screenshot of the page, that we give back to the model to <em>see</em> the result of the action on the page.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(FunctionCall<span style="color:#bbb"> </span>functionCall<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>response.<span style="color:#4070a0">functionCalls</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">switch</span><span style="color:#bbb"> </span>(functionCall.<span style="color:#4070a0">name</span>().<span style="color:#4070a0">get</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;navigate_to_url&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>page.<span style="color:#4070a0">navigate</span>((String)<span style="color:#bbb"> </span>args.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;url&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;status&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;success&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">break</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;click_at&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">break</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// ... other cases</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>This <code>switch</code> block is the heart of the integration, translating the model&rsquo;s intentions into concrete browser actions.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Supported Actions</span>
      </div>
      <div class="admonition-content">
        <p>You can check the list of
<a href="https://ai.google.dev/gemini-api/docs/computer-use#supported-actions">supported actions</a> in the documentation.
You don&rsquo;t necessarily have to implement them all, depending on your use case.
And there&rsquo;s a proposed <a href="https://github.com/google-gemini/computer-use-preview/blob/main/computers/playwright/playwright.py">implementation</a>
in Python that you can take inspiration from.
Note that there are often multiple ways to implement certain actions, like scrolling for example.
And you&rsquo;ll see in the full source code below how I implemented most of them in Java with Playwright.</p>
      </div>
    </div><h3 id="coordinate-scaling">Coordinate Scaling</h3>
<p>A critical detail in this process is coordinate handling.
The Gemini model operates on a normalized 1000x1000 grid, independent of the actual browser viewport size.
Therefore, we must scale the coordinates it provides to match our browser&rsquo;s dimensions.</p>
<p>In my case, I simply ended up using 1000x1000 for my browser window,
but I kept the scaling calculation in place, should I want to change the browser size in the future.</p>
<p>Here&rsquo;s the implementation for a <code>click_at</code> action:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;click_at&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>xClick<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>((Number)<span style="color:#bbb"> </span>args.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;x&#34;</span>)).<span style="color:#4070a0">intValue</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>yClick<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>((Number)<span style="color:#bbb"> </span>args.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;y&#34;</span>)).<span style="color:#4070a0">intValue</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>scaledCoordForClicking<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ScaledCoord(xClick,<span style="color:#bbb"> </span>yClick).<span style="color:#4070a0">scaleTo</span>(WIDTH,<span style="color:#bbb"> </span>HEIGHT);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>page.<span style="color:#4070a0">mouse</span>().<span style="color:#4070a0">click</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>scaledCoordForClicking.<span style="color:#4070a0">x</span>,<span style="color:#bbb"> </span>scaledCoordForClicking.<span style="color:#4070a0">y</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;status&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;success&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">break</span>;<span style="color:#bbb">
</span></span></span></code></pre></div><p>This is accomplished with a simple <code>ScaledCoord</code> record:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">ScaledCoord</span>(<span style="color:#902000">int</span><span style="color:#bbb"> </span>x,<span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>y)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>ScaledCoord<span style="color:#bbb"> </span><span style="color:#06287e">scaleTo</span>(<span style="color:#902000">int</span><span style="color:#bbb"> </span>width,<span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>height)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ScaledCoord(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>(<span style="color:#902000">int</span>)<span style="color:#bbb"> </span>(<span style="color:#007020;font-weight:bold">this</span>.<span style="color:#4070a0">x</span><span style="color:#bbb"> </span><span style="color:#666">/</span><span style="color:#bbb"> </span>1000.<span style="color:#4070a0">0</span><span style="color:#bbb"> </span><span style="color:#666">*</span><span style="color:#bbb"> </span>width),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>(<span style="color:#902000">int</span>)<span style="color:#bbb"> </span>(<span style="color:#007020;font-weight:bold">this</span>.<span style="color:#4070a0">y</span><span style="color:#bbb"> </span><span style="color:#666">/</span><span style="color:#bbb"> </span>1000.<span style="color:#4070a0">0</span><span style="color:#bbb"> </span><span style="color:#666">*</span><span style="color:#bbb"> </span>height)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><h3 id="accepting-safety-decisions">Accepting Safety Decisions</h3>
<p>For certain actions, the model might require a confirmation before proceeding.
This is a safety feature to prevent unintended consequences, for example when dealing with sensitive information or performing critical operations.
The model will include a <code>safety_decision</code> field in its response, indicating that a confirmation is needed.
In a real-world application, you should prompt the user for their approval.
However, for the purpose of this demonstration, the code at the bottom of this article automatically acknowledges these safety decisions.
This is implemented by checking for the presence of the <code>safety_decision</code> field in the function call arguments
and then adding a <code>safety_acknowledgement</code> field with the value <code>true</code> to the function response, as you can see in the provided source code.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(FunctionCall<span style="color:#bbb"> </span>functionCall<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>response.<span style="color:#4070a0">functionCalls</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Map<span style="color:#666">&lt;</span>String,<span style="color:#bbb"> </span>Object<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>args<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>functionCall.<span style="color:#4070a0">args</span>().<span style="color:#4070a0">get</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Map<span style="color:#666">&lt;</span>String,<span style="color:#bbb"> </span>Object<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>result<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>HashMap<span style="color:#666">&lt;&gt;</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(args.<span style="color:#4070a0">containsKey</span>(<span style="color:#4070a0">&#34;safety_decision&#34;</span>))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;safety_acknowledgement&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;true&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>This <code>safety_decision</code> request often comes when you have to accept things like cookies policy, and other pop-ups.
You can learn more about this in the <a href="https://ai.google.dev/gemini-api/docs/computer-use#safety-decisions">documentation</a>.</p>
<h3 id="closing-the-loop">Closing the Loop</h3>
<p>After executing an action, we must inform the model of the outcome.
This is done by adding a <code>function_response</code> containing the result to the conversation history.
We also give the model the screenshot, so it <em>sees</em> the updated page.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;url&#34;</span>,<span style="color:#bbb"> </span>page.<span style="color:#4070a0">url</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// wait to ensure the page has fully rendered after the action</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>sleep(1000);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#902000">byte</span><span style="color:#666">[]</span><span style="color:#bbb"> </span>screenshot<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>page.<span style="color:#4070a0">screenshot</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// also saving the screenshot locally for debugging purpose</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Page.<span style="color:#4070a0">ScreenshotOptions</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setPath</span>(Paths.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;screenshot-&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>index<span style="color:#666">++</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;.png&#34;</span>)));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>history.<span style="color:#4070a0">add</span>(Content.<span style="color:#4070a0">fromParts</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Part.<span style="color:#4070a0">fromFunctionResponse</span>(functionCall.<span style="color:#4070a0">name</span>().<span style="color:#4070a0">get</span>(),<span style="color:#bbb"> </span>result),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Part.<span style="color:#4070a0">fromBytes</span>(screenshot,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;image/png&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>));<span style="color:#bbb">
</span></span></span></code></pre></div><p>We then call <code>generateContent</code> again with the updated history,
including the <strong>result of the action</strong> as well as the <strong>screenshot of the page</strong>,
and the loop continues until the model determines the task is finished and stops returning function calls.
At that point, we can print the final text-based response.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response.<span style="color:#4070a0">text</span>());<span style="color:#bbb">
</span></span></span></code></pre></div>
    <div class="admonition important">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>
        <span>Important</span>
      </div>
      <div class="admonition-content">
        <p>You <strong>must always take a screenshot after each action</strong>, and <strong>send it to the model</strong> each time.
Otherwise, the Computer Use model is in the dark, and doesn&rsquo;t know what&rsquo;s going on in the browser.</p>
      </div>
    </div><p>This concludes our tour of how to use the Gemini 2.5 Pro Computer Use model with Java and Playwright.
Now, before having a look at the full source code at the bottom of the article,
let&rsquo;s have a look at a few example use cases I tried.</p>
<h2 id="example-browser-use-requests">Example Browser Use Requests</h2>
<h3 id="playing-a-button-clicking-game">Playing a Button Clicking Game</h3>
<p>When working on implementing the Playwright Chromium functions to echo the actions requested by the model,
at first, I wasn&rsquo;t sure if my logic for handling button clicks was correct (in particular the coordinate scaling).
So I wanted to double check that it was working fine, and for that purpose,
I created a simple <a href="https://storage.googleapis.com/public-bucket-for-demos/button.html?ne">button clicking game</a>:
Each time you click on the red button, the button moves randomly on the page, and a score shows the number of clicks.
(Feel free to play it, it&rsquo;s boring stupid!)</p>
<p>I asked Gemini Computer Use to go to that game page, click the button 10 times, and respond with the final score at the end.
Logically, clicking 10 times would give a score of 10!</p>
<blockquote>
<p>Go to this red button clicking game page:
<a href="https://storage.googleapis.com/public-bucket-for-demos/button.html">https://storage.googleapis.com/public-bucket-for-demos/button.html</a>
Click 10 times the red button.
Return the score displayed in the top left hand corner.</p></blockquote>
<p>This little animated GIF shows you the button moving around and the score increasing:</p>

<details>
  <summary>Click to see the animation</summary>
  <p><figure>
  <a href="#img-26f429ebeac370693dca85a8a78378df">
    <img src="/img/gemini/browser-use/animation-red-button.gif"
      alt="button clicking game animation"
       />
  </a>
  <figcaption>button clicking game animation</figcaption>
</figure>
<div class="lightbox" id="img-26f429ebeac370693dca85a8a78378df">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/browser-use/animation-red-button.gif"
    alt="button clicking game animation"
     />
  <div class="lightbox-caption">button clicking game animation</div>
</div>
</p>
</details>

<p>Here&rsquo;s the model&rsquo;s final answer:</p>
<blockquote>
<p>I have evaluated step 12. I clicked the button and the score increased to 10.
I have clicked the button 10 times and the score is now 10, as displayed in the top left corner.</p></blockquote>
<h3 id="searching-an-article-on-my-blog">Searching an Article on my blog</h3>
<p>I asked Gemini Computer Use to find a particular article on my blog:</p>
<blockquote>
<p>Find an article on glaforge.dev on
how to use Nano Banana in an ADK agent</p></blockquote>
<p>Let&rsquo;s see that in action in this other animation:</p>

<details>
  <summary>Click to see the animation</summary>
  <p><figure>
  <a href="#img-cee723415ca2c2e244ee427c4968a19a">
    <img src="/img/gemini/browser-use/animation-blog-article-search.gif"
      alt="Searching an article on my blog"
       />
  </a>
  <figcaption>Searching an article on my blog</figcaption>
</figure>
<div class="lightbox" id="img-cee723415ca2c2e244ee427c4968a19a">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/browser-use/animation-blog-article-search.gif"
    alt="Searching an article on my blog"
     />
  <div class="lightbox-caption">Searching an article on my blog</div>
</div>
</p>
</details>

<p>Here&rsquo;s the model&rsquo;s final answer:</p>
<pre tabindex="0"><code>I have evaluated step 18 and scrolled down further. The article
presents the code for `NanoBananaCreativeAgent` and highlights
the line `.model(&#34;gemini-2.5-flash-image-preview&#34;)` as key for
telling the ADK to route requests to the Nano Banana
(Gemini 2.5 Flash Image) model for image generation within the agent.

It also mentions an `.afterModelCallback` part that it will zoom
in on. This article clearly explains how to configure the ADK
agent to use the Nano Banana model.

I have found the information requested.

The article &#34;Creative Java AI agents with ADK and Nano Banana&#34; on
glaforge.dev explains how to use the &#34;Nano Banana&#34; model
(gemini-2.5-flash-image-preview) within an ADK agent.

It is done by specifying `.model(&#34;gemini-2.5-flash-image-preview&#34;)`
when building the `LlmAgent` using `LlmAgent.builder()`
within the agent&#39;s definition.
The article also goes into handling the image output
using `.afterModelCallback`.
</code></pre><h3 id="finding-an-item-on-amazon">Finding an Item on Amazon</h3>
<p>This time, I asked Gemini Computer Use:</p>
<blockquote>
<p>Find the tallest Stitch plushie under €100 on Amazon.fr
and give me the URL of the product.
Be sure it&rsquo;s not Angel the pink one.</p></blockquote>
<p>It found a nice Stitch plushie!</p>

<details>
  <summary>Click to see the animation</summary>
  <p><figure>
  <a href="#img-40bf26488b2747b51a6ffd648cb16d8d">
    <img src="/img/gemini/browser-use/animation-stitch.gif"
      alt="A big Stitch plushie found on Amazon"
       />
  </a>
  <figcaption>A big Stitch plushie found on Amazon</figcaption>
</figure>
<div class="lightbox" id="img-40bf26488b2747b51a6ffd648cb16d8d">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/browser-use/animation-stitch.gif"
    alt="A big Stitch plushie found on Amazon"
     />
  <div class="lightbox-caption">A big Stitch plushie found on Amazon</div>
</div>
</p>
</details>

<h2 id="the-full-source-code">The Full Source Code</h2>
<p>Tim to reveal the complete source code!
Feel free to adapt it to your needs, or to further expand the browser actions supported.</p>

<details>
  <summary>Click to see the full source code</summary>
  <div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.genai.Client</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.genai.types.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.microsoft.playwright.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.net.URLEncoder</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.nio.charset.StandardCharsets</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.nio.file.Paths</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.ArrayList</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.HashMap</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.List</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.Map</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">main</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(Playwright<span style="color:#bbb"> </span>playwright<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Playwright.<span style="color:#4070a0">create</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>BrowserType<span style="color:#bbb"> </span>chromium<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>playwright.<span style="color:#4070a0">chromium</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Browser<span style="color:#bbb"> </span>browser<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>chromium.<span style="color:#4070a0">launch</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>BrowserType.<span style="color:#4070a0">LaunchOptions</span>().<span style="color:#4070a0">setHeadless</span>(<span style="color:#007020;font-weight:bold">true</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>WIDTH<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>1000;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>HEIGHT<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>1000;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>BrowserContext<span style="color:#bbb"> </span>context<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>browser.<span style="color:#4070a0">newContext</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Browser.<span style="color:#4070a0">NewContextOptions</span>().<span style="color:#4070a0">setViewportSize</span>(WIDTH,<span style="color:#bbb"> </span>HEIGHT));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Page<span style="color:#bbb"> </span>page<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>context.<span style="color:#4070a0">newPage</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(Client<span style="color:#bbb"> </span>client<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Client.<span style="color:#4070a0">Builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>List<span style="color:#666">&lt;</span>Content<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>history<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ArrayList<span style="color:#666">&lt;&gt;</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>Content<span style="color:#bbb"> </span>initialContent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Content.<span style="color:#4070a0">fromParts</span>(Part.<span style="color:#4070a0">fromText</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                    Find an article on glaforge.dev on how to use Nano Banana in an ADK agent
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                    &#34;&#34;&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>history.<span style="color:#4070a0">add</span>(initialContent);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>GenerateContentResponse<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>index<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>0;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">while</span><span style="color:#bbb"> </span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">models</span>.<span style="color:#4070a0">generateContent</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span><span style="color:#4070a0">&#34;gemini-2.5-computer-use-preview-10-2025&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>history,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>GenerateContentConfig.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span>.<span style="color:#4070a0">tools</span>(Tool.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>.<span style="color:#4070a0">computerUse</span>(ComputerUse.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span>.<span style="color:#4070a0">environment</span>(Environment.<span style="color:#4070a0">Known</span>.<span style="color:#4070a0">ENVIRONMENT_BROWSER</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span>.<span style="color:#4070a0">build</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(response.<span style="color:#4070a0">functionCalls</span>()<span style="color:#bbb"> </span><span style="color:#666">==</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span><span style="color:#666">||</span><span style="color:#bbb"> </span>response.<span style="color:#4070a0">functionCalls</span>().<span style="color:#4070a0">isEmpty</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span><span style="color:#007020;font-weight:bold">break</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(<span style="color:#666">!</span>response.<span style="color:#4070a0">candidates</span>().<span style="color:#4070a0">isEmpty</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>history.<span style="color:#4070a0">add</span>(response.<span style="color:#4070a0">candidates</span>().<span style="color:#4070a0">get</span>().<span style="color:#4070a0">get</span>(0).<span style="color:#4070a0">content</span>().<span style="color:#4070a0">get</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(FunctionCall<span style="color:#bbb"> </span>functionCall<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>response.<span style="color:#4070a0">functionCalls</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>Map<span style="color:#666">&lt;</span>String,<span style="color:#bbb"> </span>Object<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>args<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>functionCall.<span style="color:#4070a0">args</span>().<span style="color:#4070a0">get</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;==&gt; &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>functionCall.<span style="color:#4070a0">name</span>().<span style="color:#4070a0">get</span>()<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34; &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>args);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>Map<span style="color:#666">&lt;</span>String,<span style="color:#bbb"> </span>Object<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>result<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>HashMap<span style="color:#666">&lt;&gt;</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(args.<span style="color:#4070a0">containsKey</span>(<span style="color:#4070a0">&#34;safety_decision&#34;</span>))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;safety_acknowledgement&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;true&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span><span style="color:#007020;font-weight:bold">switch</span><span style="color:#bbb"> </span>(functionCall.<span style="color:#4070a0">name</span>().<span style="color:#4070a0">get</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;open_web_browser&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;status&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;success&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">break</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;navigate&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;navigate_to_url&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>page.<span style="color:#4070a0">navigate</span>((String)<span style="color:#bbb"> </span>args.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;url&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;status&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;success&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">break</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;go_back&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>page.<span style="color:#4070a0">goBack</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;status&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;success&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">break</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;get_page_content&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>String<span style="color:#bbb"> </span>pageContent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>page.<span style="color:#4070a0">content</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(pageContent.<span style="color:#4070a0">length</span>()<span style="color:#bbb"> </span><span style="color:#666">&gt;</span><span style="color:#bbb"> </span>20000)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span>pageContent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>pageContent.<span style="color:#4070a0">substring</span>(0,<span style="color:#bbb"> </span>20000);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;content&#34;</span>,<span style="color:#bbb"> </span>pageContent);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">break</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;click_element&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span>page.<span style="color:#4070a0">click</span>((String)<span style="color:#bbb"> </span>args.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;selector&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;status&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;success&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">catch</span><span style="color:#bbb"> </span>(PlaywrightException<span style="color:#bbb"> </span>e)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;status&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;error&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;message&#34;</span>,<span style="color:#bbb"> </span>e.<span style="color:#4070a0">getMessage</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">break</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;click_at&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>xClick<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>((Number)<span style="color:#bbb"> </span>args.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;x&#34;</span>)).<span style="color:#4070a0">intValue</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>yClick<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>((Number)<span style="color:#bbb"> </span>args.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;y&#34;</span>)).<span style="color:#4070a0">intValue</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>scaledCoordForClicking<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ScaledCoord(xClick,<span style="color:#bbb"> </span>yClick).<span style="color:#4070a0">scaleTo</span>(WIDTH,<span style="color:#bbb"> </span>HEIGHT);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>page.<span style="color:#4070a0">mouse</span>().<span style="color:#4070a0">click</span>(scaledCoordForClicking.<span style="color:#4070a0">x</span>,<span style="color:#bbb"> </span>scaledCoordForClicking.<span style="color:#4070a0">y</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;status&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;success&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">break</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;type_text&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>String<span style="color:#bbb"> </span>text<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>(String)<span style="color:#bbb"> </span>args.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;text&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>String<span style="color:#bbb"> </span>selectorForTyping<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>(String)<span style="color:#bbb"> </span>args.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;selector&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(selectorForTyping<span style="color:#bbb"> </span><span style="color:#666">!=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span>page.<span style="color:#4070a0">locator</span>(selectorForTyping).<span style="color:#4070a0">type</span>(text);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span>page.<span style="color:#4070a0">keyboard</span>().<span style="color:#4070a0">type</span>(text);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;status&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;success&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">break</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;type_text_at&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>String<span style="color:#bbb"> </span>text_to_type<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>(String)<span style="color:#bbb"> </span>args.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;text&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#902000">boolean</span><span style="color:#bbb"> </span>press_enter<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>(Boolean)<span style="color:#bbb"> </span>args.<span style="color:#4070a0">getOrDefault</span>(<span style="color:#4070a0">&#34;press_enter&#34;</span>,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">false</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>xType<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>((Number)<span style="color:#bbb"> </span>args.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;x&#34;</span>)).<span style="color:#4070a0">intValue</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>yType<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>((Number)<span style="color:#bbb"> </span>args.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;y&#34;</span>)).<span style="color:#4070a0">intValue</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>scaledCoordForTyping<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ScaledCoord(xType,<span style="color:#bbb"> </span>yType).<span style="color:#4070a0">scaleTo</span>(WIDTH,<span style="color:#bbb"> </span>HEIGHT);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>page.<span style="color:#4070a0">mouse</span>().<span style="color:#4070a0">click</span>(scaledCoordForTyping.<span style="color:#4070a0">x</span>,<span style="color:#bbb"> </span>scaledCoordForTyping.<span style="color:#4070a0">y</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>page.<span style="color:#4070a0">keyboard</span>().<span style="color:#4070a0">type</span>(text_to_type);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(press_enter)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span>page.<span style="color:#4070a0">keyboard</span>().<span style="color:#4070a0">press</span>(<span style="color:#4070a0">&#34;Enter&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;status&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;success&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">break</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;scroll_document&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>String<span style="color:#bbb"> </span>direction<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>(String)<span style="color:#bbb"> </span>args.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;direction&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>magnitude<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>800;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(args.<span style="color:#4070a0">containsKey</span>(<span style="color:#4070a0">&#34;magnitude&#34;</span>))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span>magnitude<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>((Number)<span style="color:#bbb"> </span>args.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;magnitude&#34;</span>)).<span style="color:#4070a0">intValue</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(<span style="color:#4070a0">&#34;down&#34;</span>.<span style="color:#4070a0">equals</span>(direction))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span>page.<span style="color:#4070a0">evaluate</span>(<span style="color:#4070a0">&#34;window.scrollBy(0, &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>magnitude<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;)&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(<span style="color:#4070a0">&#34;up&#34;</span>.<span style="color:#4070a0">equals</span>(direction))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span>page.<span style="color:#4070a0">evaluate</span>(<span style="color:#4070a0">&#34;window.scrollBy(0, -&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>magnitude<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;)&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;status&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;success&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">break</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;search&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>String<span style="color:#bbb"> </span>query<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>(String)<span style="color:#bbb"> </span>args.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;query&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(query<span style="color:#bbb"> </span><span style="color:#666">!=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span><span style="color:#666">&amp;&amp;</span><span style="color:#bbb"> </span><span style="color:#666">!</span>query.<span style="color:#4070a0">isEmpty</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span>page.<span style="color:#4070a0">navigate</span>(<span style="color:#4070a0">&#34;https://www.google.com/search?q=&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                        </span>URLEncoder.<span style="color:#4070a0">encode</span>(query,<span style="color:#bbb"> </span>StandardCharsets.<span style="color:#4070a0">UTF_8</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;status&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;success&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;status&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;unsupported function&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;message&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;search function requires a query argument.&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">break</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;take_screenshot&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#902000">byte</span><span style="color:#666">[]</span><span style="color:#bbb"> </span>screenshotBytes<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>page.<span style="color:#4070a0">screenshot</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;screenshot-image-bytes&#34;</span>,<span style="color:#bbb"> </span>screenshotBytes);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;status&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;success&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">break</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;wait_5_seconds&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>sleep(5000);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;status&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;success&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">break</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#007020;font-weight:bold">default</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;error&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;unsupported function&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>result.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;url&#34;</span>,<span style="color:#bbb"> </span>page.<span style="color:#4070a0">url</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>sleep(1000);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span><span style="color:#902000">byte</span><span style="color:#666">[]</span><span style="color:#bbb"> </span>screenshot<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>page.<span style="color:#4070a0">screenshot</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Page.<span style="color:#4070a0">ScreenshotOptions</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                </span>.<span style="color:#4070a0">setPath</span>(Paths.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;screenshot-&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>index<span style="color:#666">++</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;.png&#34;</span>)));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>history.<span style="color:#4070a0">add</span>(Content.<span style="color:#4070a0">fromParts</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>Part.<span style="color:#4070a0">fromFunctionResponse</span>(functionCall.<span style="color:#4070a0">name</span>().<span style="color:#4070a0">get</span>(),<span style="color:#bbb"> </span>result),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>Part.<span style="color:#4070a0">fromBytes</span>(screenshot,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;image/png&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response.<span style="color:#4070a0">text</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">finally</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>browser.<span style="color:#4070a0">close</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">ScaledCoord</span>(<span style="color:#902000">int</span><span style="color:#bbb"> </span>x,<span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>y)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>ScaledCoord<span style="color:#bbb"> </span><span style="color:#06287e">scaleTo</span>(<span style="color:#902000">int</span><span style="color:#bbb"> </span>width,<span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>height)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ScaledCoord(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>(<span style="color:#902000">int</span>)<span style="color:#bbb"> </span>(<span style="color:#007020;font-weight:bold">this</span>.<span style="color:#4070a0">x</span><span style="color:#bbb"> </span><span style="color:#666">/</span><span style="color:#bbb"> </span>1000.<span style="color:#4070a0">0</span><span style="color:#bbb"> </span><span style="color:#666">*</span><span style="color:#bbb"> </span>width),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>(<span style="color:#902000">int</span>)<span style="color:#bbb"> </span>(<span style="color:#007020;font-weight:bold">this</span>.<span style="color:#4070a0">y</span><span style="color:#bbb"> </span><span style="color:#666">/</span><span style="color:#bbb"> </span>1000.<span style="color:#4070a0">0</span><span style="color:#bbb"> </span><span style="color:#666">*</span><span style="color:#bbb"> </span>height)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">sleep</span>(<span style="color:#902000">int</span><span style="color:#bbb"> </span>milliseconds)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Thread.<span style="color:#4070a0">sleep</span>(milliseconds);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">catch</span><span style="color:#bbb"> </span>(InterruptedException<span style="color:#bbb"> </span>e)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// do nothing</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div></details>

<h2 id="conclusion">Conclusion</h2>
<p>My experiments with the Gemini 2.5 Pro Computer Use model have been insightful, revealing both its potential and some of its limitations.</p>
<p>One of the first things you&rsquo;ll notice is the <strong>pacing</strong>.
Each turn in the agent loop, model call, and action execution, takes time.
A multi-step task like finding a product on an e-commerce site requires patience, as the process unfolds deliberately, one step at a time.
It&rsquo;s a good idea to wait a second before taking a screenshot, to be sure the page has fully rendered after the last action.
Otherwise you could feed a blank or half-blank screen back to the model, which won&rsquo;t be helpful.
So you would use a computer-use model probably more for asynchronous background tasks, and not for an immediate response.</p>
<p>A significant real-world challenge is the prevalence of <strong>cookie consent banners and other pop-ups</strong>.
But the model is able to click the right <em>&ldquo;accept&rdquo;</em> buttons here and there, to get rid of them, and focus on the task at hand.
Captchas can also get in the way, because websites notice that this is an automated agent at play.
But there&rsquo;s a way to <a href="https://ai.google.dev/gemini-api/docs/computer-use#safety-decisions">automate what they call <em>safety decisions</em></a>
(which is what I implemented in the code, by auto-acknowledgement).</p>
<p>Interestingly, in one of my many experiments, in moments of apparent frustration,
the model decided to <strong>abandon the target website and default to a Google search</strong>,
to find the information on the website it couldn&rsquo;t find by scrolling and clicking around!</p>
<p>In summary, once you acknowledge some of the potential challenges, this is clearly a fascinating technology with a promising <em>agentic</em> future!
Imagine web agents able to handle mundane but boring web-based tasks for you, saving you precious time?
That&rsquo;s definitely something worth investigating!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A Javelit frontend for an ADK agent</title><link>https://glaforge.dev/posts/2025/10/26/a-javelit-frontend-for-an-adk-agent/</link><pubDate>Sun, 26 Oct 2025 18:06:13 +0100</pubDate><guid>https://glaforge.dev/posts/2025/10/26/a-javelit-frontend-for-an-adk-agent/</guid><description>&lt;p>Continuing my journey with &lt;a href="https://javelit.io">Javelit&lt;/a>,
after creating a &lt;a href="https://glaforge.dev/posts/2025/10/24/javelit-to-create-quick-interactive-app-frontends-in-java/">frontend for &lt;em>&amp;ldquo;Nano Banana&amp;rdquo;&lt;/em> to generate images&lt;/a>
and a &lt;a href="https://glaforge.dev/posts/2025/10/25/creating-a-javelit-chat-interface-for-langchain4j/">chat interface for a LangChain4j-based Gemini chat model&lt;/a>,
I decided to see how I could integrate an &lt;a href="https://github.com/google/adk-java">ADK&lt;/a> agent with a Javelit frontend.&lt;/p>
&lt;h2 id="the-javelit-interface-for-an-adk-search-agent">The Javelit interface for an ADK search agent&lt;/h2>
&lt;p>&lt;figure>
&lt;a href="#img-03ed9e9178afe3608f5ea337720e6971">
&lt;img src="https://glaforge.dev/img/adk/adk-javelit.png"
alt="A Javelit interface for an ADK search agent"
/>
&lt;/a>
&lt;figcaption>A Javelit interface for an ADK search agent&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-03ed9e9178afe3608f5ea337720e6971">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/adk/adk-javelit.png"
alt="A Javelit interface for an ADK search agent"
/>
&lt;div class="lightbox-caption">A Javelit interface for an ADK search agent&lt;/div>
&lt;/div>
&lt;/p></description><content:encoded>
<![CDATA[<p>Continuing my journey with <a href="https://javelit.io">Javelit</a>,
after creating a <a href="https://glaforge.dev/posts/2025/10/24/javelit-to-create-quick-interactive-app-frontends-in-java/">frontend for <em>&ldquo;Nano Banana&rdquo;</em> to generate images</a>
and a <a href="https://glaforge.dev/posts/2025/10/25/creating-a-javelit-chat-interface-for-langchain4j/">chat interface for a LangChain4j-based Gemini chat model</a>,
I decided to see how I could integrate an <a href="https://github.com/google/adk-java">ADK</a> agent with a Javelit frontend.</p>
<h2 id="the-javelit-interface-for-an-adk-search-agent">The Javelit interface for an ADK search agent</h2>
<p><figure>
  <a href="#img-03ed9e9178afe3608f5ea337720e6971">
    <img src="/img/adk/adk-javelit.png"
      alt="A Javelit interface for an ADK search agent"
       />
  </a>
  <figcaption>A Javelit interface for an ADK search agent</figcaption>
</figure>
<div class="lightbox" id="img-03ed9e9178afe3608f5ea337720e6971">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/adk/adk-javelit.png"
    alt="A Javelit interface for an ADK search agent"
     />
  <div class="lightbox-caption">A Javelit interface for an ADK search agent</div>
</div>
</p>
<p>The key ingredients of this interface:</p>
<ul>
<li>a title <em>(with some emojis &#x1f603;)</em></li>
<li>a container that displays the agent&rsquo;s answer</li>
<li>a text input field to enter the search query</li>
</ul>
<h2 id="the-adk-agent">The ADK agent</h2>
<p>For the purpose of this article, I built a simple search agent, with a couple of search tools:</p>
<ul>
<li>a <strong>Google Search</strong> tool</li>
<li>a <strong>Google Maps</strong> tool</li>
</ul>
<p>So you can search for up-to-date information on Google Search,
as well as details about landmarks, points of interest, restaurants, etc., via Google Maps.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>I recently <a href="https://github.com/google/adk-java/pull/534">contributed</a>
the <code>GoogleMapsTool</code> to ADK for Java, so it&rsquo;s not yet available in a public version,
you&rsquo;ll have to wait for the next release (or even build it from sources!)
to be able to use it.</p>
      </div>
    </div><p>Let&rsquo;s have a closer look at the agent code:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>LlmAgent<span style="color:#bbb"> </span>agent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;gemini-search-agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You are a helpful search assistant,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        able to search the web and Google Maps.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        When a user asks for research,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        be sure to use the appropriate tools detailed below.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Use the `google_search` tool
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        to search for up-to-date information.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Use the `google_maps` tool
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        to search for geographical information.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GoogleSearchTool(),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GoogleMapsTool()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>This is a simple agent, with instructions detailing the tools at its disposal and wiring the two tools.</p>
<p>Next, to interact with the agent, we need some setup: we&rsquo;ll need a <code>Runner</code> and prepare a <code>Session</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>InMemorySessionService<span style="color:#bbb"> </span>sessionService<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>InMemorySessionService();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>InMemoryArtifactService<span style="color:#bbb"> </span>artifactService<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>InMemoryArtifactService();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Runner<span style="color:#bbb"> </span>runner<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Runner(agent,<span style="color:#bbb"> </span>agent.<span style="color:#4070a0">name</span>(),<span style="color:#bbb"> </span>artifactService,<span style="color:#bbb"> </span>sessionService,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>appName<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>runner.<span style="color:#4070a0">appName</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>userId<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>UUID.<span style="color:#4070a0">randomUUID</span>().<span style="color:#4070a0">toString</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Session<span style="color:#bbb"> </span>session<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>runner<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">sessionService</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">createSession</span>(appName,<span style="color:#bbb"> </span>userId)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">blockingGet</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>To interact with this agent via the <code>Runner</code>&rsquo;s <code>runAsync()</code> method,
we need to keep the agent, the session, and the user ID around, so I created a <code>record</code> to hold them,
and created a method to wrap it all:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">AgentRunnerSession</span>(Runner<span style="color:#bbb"> </span>runner,<span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>userId,<span style="color:#bbb"> </span>Session<span style="color:#bbb"> </span>session)<span style="color:#bbb"> </span>{<span style="color:#bbb"> </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>AgentRunnerSession<span style="color:#bbb"> </span><span style="color:#06287e">getAgentSession</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">//... agent definition above...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>AgentRunnerSession(runner,<span style="color:#bbb"> </span>userId,<span style="color:#bbb"> </span>session);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="building-the-ui-and-saving-the-agent-in-the-javelit-session">Building the UI and saving the agent in the Javelit session</h2>
<p>Like in the previous articles, the UI code layout is prepared in the class&rsquo; <code>main</code> method:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">main</span>(String<span style="color:#666">[]</span><span style="color:#bbb"> </span>args)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#60a0b0;font-style:italic">// Javelit UI layout</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>With Javelit, the UI component and layout code is re-run each time there&rsquo;s an interaction from the user.
In order to keep the conversation going with the agent, we need to store it in Javelit&rsquo;s session state.
It&rsquo;s created the first time thanks to the <code>computeIfAbsent()</code> method and retrieved upon subsequent calls:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>AgentRunnerSession<span style="color:#bbb"> </span>holder<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>(AgentRunnerSession)<span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">sessionState</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">computeIfAbsent</span>(<span style="color:#4070a0">&#34;agentRunnerSession&#34;</span>,<span style="color:#bbb"> </span>key<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>getAgentSession());<span style="color:#bbb">
</span></span></span></code></pre></div><p>We add the title component, a container to hold the agent&rsquo;s response, and a text input field for the user&rsquo;s search query:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Jt.<span style="color:#4070a0">title</span>(<span style="color:#4070a0">&#34;\uD83D\uDD0D ADK Search Agent \uD83E\uDD16\uD83E\uDDE0&#34;</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>JtContainer<span style="color:#bbb"> </span>eventContainer<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">container</span>().<span style="color:#4070a0">border</span>(<span style="color:#007020;font-weight:bold">true</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>searchQuery<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">textInput</span>(<span style="color:#4070a0">&#34;Search query&#34;</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>When the user interacts with this text input field by hitting Enter, the input value is saved in the <code>searchQuery</code> variable.
Once we have that user query, we can pass it to the agent via the <code>Runner</code>&rsquo;s <code>runAsync()</code> method.
For each event, we add a Markdown element with the content of that event:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(searchQuery<span style="color:#bbb"> </span><span style="color:#666">!=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span><span style="color:#666">&amp;&amp;</span><span style="color:#bbb"> </span><span style="color:#666">!</span>searchQuery.<span style="color:#4070a0">isEmpty</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>holder.<span style="color:#4070a0">runner</span>().<span style="color:#4070a0">runAsync</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>holder.<span style="color:#4070a0">userId</span>(),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>holder.<span style="color:#4070a0">session</span>().<span style="color:#4070a0">id</span>(),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Content.<span style="color:#4070a0">fromParts</span>(Part.<span style="color:#4070a0">fromText</span>(searchQuery)))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">blockingForEach</span>(event<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>Jt.<span style="color:#4070a0">markdown</span>(event.<span style="color:#4070a0">stringifyContent</span>()).<span style="color:#4070a0">use</span>(eventContainer);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>});<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>With this approach, we maintain the conversational state.
Although the UI isn&rsquo;t displaying the past requests and responses,
each time the user enters a query, both the query and response are kept in the agent&rsquo;s memory.
That way, if you ask for information about a restaurant, then you ask about opening times,
it remembers it&rsquo;s about this particular restaurant.</p>
<h2 id="the-whole-example">The whole example</h2>

<details>
  <summary>Click to view the whole source code</summary>
  <div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">/// usr/bin/env jbang &#34;$0&#34; &#34;$@&#34; ; exit $?</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//DEPS com.google.adk:google-adk:0.3.1-SNAPSHOT</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">package</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">adk</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.adk.agents.LlmAgent</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.adk.artifacts.InMemoryArtifactService</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.adk.runner.Runner</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.adk.sessions.InMemorySessionService</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.adk.sessions.Session</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.adk.tools.GoogleMapsTool</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.adk.tools.GoogleSearchTool</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.genai.types.Content</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.genai.types.Part</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.javelit.core.Jt</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.javelit.core.JtContainer</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.UUID</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">App</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">main</span>(String<span style="color:#666">[]</span><span style="color:#bbb"> </span>args)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>AgentRunnerSession<span style="color:#bbb"> </span>holder<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>(AgentRunnerSession)<span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">sessionState</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">computeIfAbsent</span>(<span style="color:#4070a0">&#34;holder&#34;</span>,<span style="color:#bbb"> </span>key<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>getAgentSession());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Jt.<span style="color:#4070a0">title</span>(<span style="color:#4070a0">&#34;\uD83D\uDD0D ADK Search Agent \uD83E\uDD16\uD83E\uDDE0&#34;</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>JtContainer<span style="color:#bbb"> </span>eventContainer<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">container</span>().<span style="color:#4070a0">border</span>(<span style="color:#007020;font-weight:bold">true</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>searchQuery<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">textInput</span>(<span style="color:#4070a0">&#34;Search query&#34;</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(searchQuery<span style="color:#bbb"> </span><span style="color:#666">!=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span><span style="color:#666">&amp;&amp;</span><span style="color:#bbb"> </span><span style="color:#666">!</span>searchQuery.<span style="color:#4070a0">isEmpty</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>holder.<span style="color:#4070a0">runner</span>().<span style="color:#4070a0">runAsync</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>holder.<span style="color:#4070a0">userId</span>(),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>holder.<span style="color:#4070a0">session</span>().<span style="color:#4070a0">id</span>(),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>Content.<span style="color:#4070a0">fromParts</span>(Part.<span style="color:#4070a0">fromText</span>(searchQuery))).<span style="color:#4070a0">blockingForEach</span>(event<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Jt.<span style="color:#4070a0">markdown</span>(event.<span style="color:#4070a0">stringifyContent</span>()).<span style="color:#4070a0">use</span>(eventContainer);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>});<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">AgentRunnerSession</span>(Runner<span style="color:#bbb"> </span>runner,<span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>userId,<span style="color:#bbb"> </span>Session<span style="color:#bbb"> </span>session)<span style="color:#bbb"> </span>{<span style="color:#bbb"> </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>AgentRunnerSession<span style="color:#bbb"> </span><span style="color:#06287e">getAgentSession</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>LlmAgent<span style="color:#bbb"> </span>agent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;gemini-search-agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            You are a helpful search assistant, able to search the web and Google Maps.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            When a user asks for research, be sure to use the appropriate tools detailed below.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Use the `google_search` tool to search for up-to-date information.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Use the `google_maps` tool to search for geographical information.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">tools</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GoogleSearchTool(),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GoogleMapsTool()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>InMemorySessionService<span style="color:#bbb"> </span>sessionService<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>InMemorySessionService();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>InMemoryArtifactService<span style="color:#bbb"> </span>artifactService<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>InMemoryArtifactService();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Runner<span style="color:#bbb"> </span>runner<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Runner(agent,<span style="color:#bbb"> </span>agent.<span style="color:#4070a0">name</span>(),<span style="color:#bbb"> </span>artifactService,<span style="color:#bbb"> </span>sessionService,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>appName<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>runner.<span style="color:#4070a0">appName</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>userId<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>UUID.<span style="color:#4070a0">randomUUID</span>().<span style="color:#4070a0">toString</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Session<span style="color:#bbb"> </span>session<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>runner<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">sessionService</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">createSession</span>(appName,<span style="color:#bbb"> </span>userId)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">blockingGet</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>AgentRunnerSession(runner,<span style="color:#bbb"> </span>userId,<span style="color:#bbb"> </span>session);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div></details>

<h2 id="summary">Summary</h2>
<p>Another <a href="https://javelit.io/">Javelit</a> integration on the books, this time with <a href="https://github.com/google/adk-java">ADK for Java</a>.</p>
<p>At first, I was a bit surprised by the library&rsquo;s unusual approach,
compared to more event-driven or reactive web frameworks.
However, I&rsquo;m liking the simplicity of Javelit for quickly building a web frontend to let me experiment with ideas.</p>
<p>There are still many more features or components I&rsquo;d like to explore (like charts, multi-page components, etc.)
So you might see me write a few more articles, or use it more in my own presentations and workshops!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Creating a Javelit chat interface for LangChain4j</title><link>https://glaforge.dev/posts/2025/10/25/creating-a-javelit-chat-interface-for-langchain4j/</link><pubDate>Sat, 25 Oct 2025 17:11:19 +0200</pubDate><guid>https://glaforge.dev/posts/2025/10/25/creating-a-javelit-chat-interface-for-langchain4j/</guid><description>&lt;p>Yesterday, I uncovered the &lt;a href="https://javelit.io">Javelit&lt;/a> project in this
&lt;a href="https://glaforge.dev/posts/2025/10/24/javelit-to-create-quick-interactive-app-frontends-in-java/">article&lt;/a>
where I built a small frontend to create and edit images
with Google&amp;rsquo;s &lt;a href="https://aistudio.google.com/models/gemini-2-5-flash-image">Nano Banana&lt;/a> image model.&lt;/p>
&lt;link rel="stylesheet" href="https://glaforge.dev/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&amp;#43;AD&amp;#43;9EeYXUTm&amp;#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
&lt;div class="admonition info">
&lt;div class="admonition-header">&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">&lt;path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/>&lt;/svg>
&lt;span>Javelit&lt;/span>
&lt;/div>
&lt;div class="admonition-content">
&lt;p>&lt;strong>Javelit&lt;/strong> is an open source project inspired by Streamlit from the Python ecosystem
to enable rapid prototyping and deployment of applications in Java.&lt;/p></description><content:encoded>
<![CDATA[<p>Yesterday, I uncovered the <a href="https://javelit.io">Javelit</a> project in this
<a href="https://glaforge.dev/posts/2025/10/24/javelit-to-create-quick-interactive-app-frontends-in-java/">article</a>
where I built a small frontend to create and edit images
with Google&rsquo;s <a href="https://aistudio.google.com/models/gemini-2-5-flash-image">Nano Banana</a> image model.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Javelit</span>
      </div>
      <div class="admonition-content">
        <p><strong>Javelit</strong> is an open source project inspired by Streamlit from the Python ecosystem
to enable rapid prototyping and deployment of applications in Java.</p>
      </div>
    </div><p>Today, I want to show you another example of Javelit.
This time, I&rsquo;m creating a <strong>chat interface</strong> using <a href="https://docs.langchain4j.dev">LangChain4j</a>
with the <a href="https://docs.langchain4j.dev/integrations/language-models/google-ai-gemini">Gemini</a> chat model.</p>
<h2 id="what-we-want-to-build">What we want to build</h2>
<p><figure>
  <a href="#img-49a1f749c1fb4732aee527e40bbea6c1">
    <img src="/img/misc/javelit-langchain4j.png"
      alt="Generative AI chat interface built with Javelit, LangChain4j, and the Gemini model"
       />
  </a>
  <figcaption>Generative AI chat interface built with Javelit, LangChain4j, and the Gemini model</figcaption>
</figure>
<div class="lightbox" id="img-49a1f749c1fb4732aee527e40bbea6c1">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/javelit-langchain4j.png"
    alt="Generative AI chat interface built with Javelit, LangChain4j, and the Gemini model"
     />
  <div class="lightbox-caption">Generative AI chat interface built with Javelit, LangChain4j, and the Gemini model</div>
</div>
</p>
<p>Notice how we alternate user and AI messages, and how the text is nicely rendered from Markdown?
Let&rsquo;s see how to implement such an interface with Javelit and LangChain4j.</p>
<h2 id="lets-build-it">Let&rsquo;s build it!</h2>
<p>Feel free to use any LLM model provider, but in my example today, I&rsquo;m using Gemini:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>ChatModel<span style="color:#bbb"> </span>CHAT_MODEL<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-2.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GOOGLE_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>When using LangChain4j chat models at the low-level (not using <code>AiServices</code> or the new <em>agentic</em> module),
we keep track of chat messages via a simple <code>List</code> of <code>ChatMessage</code>.
This chat history needs to be stored in Javelit&rsquo;s session state:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>List<span style="color:#666">&lt;</span>ChatMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>chatHistory<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>(List<span style="color:#666">&lt;</span>ChatMessage<span style="color:#666">&gt;</span>)<span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">sessionState</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">computeIfAbsent</span>(<span style="color:#4070a0">&#34;chatHistory&#34;</span>,<span style="color:#bbb"> </span>(key)<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ArrayList<span style="color:#666">&lt;&gt;</span>());<span style="color:#bbb">
</span></span></span></code></pre></div><p>Let&rsquo;s give this application a title, and prepare a <em>container</em> that will receive all the chat messages from both the AI and the user:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Jt.<span style="color:#4070a0">title</span>(<span style="color:#4070a0">&#34;:coffee::parrot: LangChain4j Chat :speech_balloon:&#34;</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>JtContainer<span style="color:#bbb"> </span>msgContainer<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">container</span>().<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Notice that Javelit supports emoji code names!</p>
<p>Next, let&rsquo;s append all the messages from the chat history to the message container, alternating between AI and user messages:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(ChatMessage<span style="color:#bbb"> </span>message<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>chatHistory)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">switch</span><span style="color:#bbb"> </span>(message.<span style="color:#4070a0">type</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span>USER<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">markdown</span>(<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;:speech_balloon: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>((UserMessage)<span style="color:#bbb"> </span>message).<span style="color:#4070a0">singleText</span>()).<span style="color:#4070a0">use</span>(msgContainer);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span>AI<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">markdown</span>(<span style="color:#4070a0">&#34;:robot: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>((AiMessage)<span style="color:#bbb"> </span>message).<span style="color:#4070a0">text</span>()).<span style="color:#4070a0">use</span>(msgContainer);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Again, we&rsquo;re using the <code>Jt.markdown()</code> component that outputs Markdown.
Which is nice since LLMs love to return Markdown responses!</p>
<p>It&rsquo;s time to get some input message from the user, with the <code>Jt.textInput()</code> component, and save its value in a local variable:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>String<span style="color:#bbb"> </span>inputMessage<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">textInput</span>(<span style="color:#4070a0">&#34;Your message:&#34;</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Let&rsquo;s update the chat history, display the user&rsquo;s message, call the Gemini chat model, and then display its response:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(inputMessage<span style="color:#bbb"> </span><span style="color:#666">!=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span><span style="color:#666">&amp;&amp;</span><span style="color:#bbb"> </span><span style="color:#666">!</span>inputMessage.<span style="color:#4070a0">trim</span>().<span style="color:#4070a0">isEmpty</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>chatHistory.<span style="color:#4070a0">add</span>(UserMessage.<span style="color:#4070a0">from</span>(inputMessage));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Jt.<span style="color:#4070a0">markdown</span>(<span style="color:#4070a0">&#34;:speech_balloon: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>inputMessage).<span style="color:#4070a0">use</span>(msgContainer);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>ChatResponse<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>CHAT_MODEL.<span style="color:#4070a0">chat</span>(chatHistory);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>chatHistory.<span style="color:#4070a0">add</span>(response.<span style="color:#4070a0">aiMessage</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Jt.<span style="color:#4070a0">markdown</span>(<span style="color:#4070a0">&#34;:robot: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>response.<span style="color:#4070a0">aiMessage</span>().<span style="color:#4070a0">text</span>()).<span style="color:#4070a0">use</span>(msgContainer);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>And that&rsquo;s it!
When the user hits the enter key, after having typed its message, it retriggers a UI refresh.
It goes over all the UI component rendering again, but uses the state to show the alternation of user/AI messages.</p>
<h2 id="the-whole-source-code">The whole source code</h2>

<details>
  <summary>Click to view the whole source code</summary>
  <div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">/// usr/bin/env jbang &#34;$0&#34; &#34;$@&#34; ; exit $?</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//DEPS dev.langchain4j:langchain4j-core:1.8.0</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//DEPS dev.langchain4j:langchain4j-google-ai-gemini:1.8.0</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.data.message.AiMessage</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.data.message.ChatMessage</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.data.message.UserMessage</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.model.chat.ChatModel</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.model.chat.response.ChatResponse</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.model.googleai.GoogleAiGeminiChatModel</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.javelit.core.Jt</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.javelit.core.JtContainer</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.ArrayList</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.List</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">App</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>ChatModel<span style="color:#bbb"> </span>CHAT_MODEL<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-2.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GOOGLE_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">main</span>(String<span style="color:#666">[]</span><span style="color:#bbb"> </span>args)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>List<span style="color:#666">&lt;</span>ChatMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>chatHistory<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>(List<span style="color:#666">&lt;</span>ChatMessage<span style="color:#666">&gt;</span>)<span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">sessionState</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">computeIfAbsent</span>(<span style="color:#4070a0">&#34;chatHistory&#34;</span>,<span style="color:#bbb"> </span>(key)<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ArrayList<span style="color:#666">&lt;&gt;</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Jt.<span style="color:#4070a0">title</span>(<span style="color:#4070a0">&#34;:coffee::parrot: LangChain4j Chat :speech_balloon:&#34;</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>JtContainer<span style="color:#bbb"> </span>msgContainer<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">container</span>().<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(ChatMessage<span style="color:#bbb"> </span>message<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>chatHistory)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#007020;font-weight:bold">switch</span><span style="color:#bbb"> </span>(message.<span style="color:#4070a0">type</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span>USER<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">markdown</span>(<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;:speech_balloon: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>((UserMessage)<span style="color:#bbb"> </span>message).<span style="color:#4070a0">singleText</span>()).<span style="color:#4070a0">use</span>(msgContainer);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span>AI<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">markdown</span>(<span style="color:#4070a0">&#34;:robot: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>((AiMessage)<span style="color:#bbb"> </span>message).<span style="color:#4070a0">text</span>()).<span style="color:#4070a0">use</span>(msgContainer);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>inputMessage<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">textInput</span>(<span style="color:#4070a0">&#34;Your message:&#34;</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(inputMessage<span style="color:#bbb"> </span><span style="color:#666">!=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span><span style="color:#666">&amp;&amp;</span><span style="color:#bbb"> </span><span style="color:#666">!</span>inputMessage.<span style="color:#4070a0">trim</span>().<span style="color:#4070a0">isEmpty</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>chatHistory.<span style="color:#4070a0">add</span>(UserMessage.<span style="color:#4070a0">from</span>(inputMessage));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>Jt.<span style="color:#4070a0">markdown</span>(<span style="color:#4070a0">&#34;:speech_balloon: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>inputMessage).<span style="color:#4070a0">use</span>(msgContainer);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>ChatResponse<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>CHAT_MODEL.<span style="color:#4070a0">chat</span>(chatHistory);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>chatHistory.<span style="color:#4070a0">add</span>(response.<span style="color:#4070a0">aiMessage</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>Jt.<span style="color:#4070a0">markdown</span>(<span style="color:#4070a0">&#34;:robot: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>response.<span style="color:#4070a0">aiMessage</span>().<span style="color:#4070a0">text</span>()).<span style="color:#4070a0">use</span>(msgContainer);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div></details>

<p>Then you can run this class (after having <a href="https://docs.javelit.io/get-started/installation">installed Javelit</a>) with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>javelit run App.java
</span></span></code></pre></div><h2 id="summary">Summary</h2>
<p>And voilà!
In this article, we&rsquo;ve managed to build a <strong>simple chat UI</strong> for
<a href="https://docs.langchain4j.dev">LangChain4j</a> chat models using the
<a href="https://javelit.io">Javelit</a> UI toolkit.
We took advantage of Javelit&rsquo;s state management, as well as the built-in markdown rendering,
as LLMs generally use markdown in their responses.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Javelit to create quick interactive app frontends in Java</title><link>https://glaforge.dev/posts/2025/10/24/javelit-to-create-quick-interactive-app-frontends-in-java/</link><pubDate>Fri, 24 Oct 2025 17:33:27 +0200</pubDate><guid>https://glaforge.dev/posts/2025/10/24/javelit-to-create-quick-interactive-app-frontends-in-java/</guid><description>&lt;p>Have you ever heard of &lt;a href="https://javelit.io/">Javelit&lt;/a>?
It&amp;rsquo;s &lt;strong>like Streamlit&lt;/strong> in the Python ecosystem, but &lt;strong>for the Java developer&lt;/strong>!
I was lucky that the project creator reached out and introduced me to this cool little tool!&lt;/p>
&lt;p>Javelit is a tool to &lt;strong>quickly build interactive app frontends in Java&lt;/strong>, particularly for data apps, but it&amp;rsquo;s not limited to them.
It helps you quickly develop rapid prototypes, with a &lt;strong>live-reload&lt;/strong> loop, so that you can quickly experiment and update the app instantly.&lt;/p></description><content:encoded>
<![CDATA[<p>Have you ever heard of <a href="https://javelit.io/">Javelit</a>?
It&rsquo;s <strong>like Streamlit</strong> in the Python ecosystem, but <strong>for the Java developer</strong>!
I was lucky that the project creator reached out and introduced me to this cool little tool!</p>
<p>Javelit is a tool to <strong>quickly build interactive app frontends in Java</strong>, particularly for data apps, but it&rsquo;s not limited to them.
It helps you quickly develop rapid prototypes, with a <strong>live-reload</strong> loop, so that you can quickly experiment and update the app instantly.</p>
<p>The way it works (and thus the way you program with it) is a little unusual, so it took me a bit of time to really get it.
But basically, as the documentation states (in the <a href="https://docs.javelit.io/get-started/fundamentals">fundamentals section</a>):</p>
<blockquote>
<p>Javelit&rsquo;s architecture allows you to write apps the same way you write plain Java methods.
To unlock this, Javelit apps have a unique data flow: any time something must be updated on the screen,
Javelit reruns your entire Java main method from top to bottom.</p></blockquote>
<p>So you have to think about it as if there were somehow a big loop around your UI code,
and Javelit redraws it whenever you modify the source code (because of the live-reload capability),
or of course, when a user interacts somehow with the app (submitting a form, clicking a button, moving a slider, etc.)</p>
<p>It&rsquo;s possible to <a href="https://docs.javelit.io/get-started/installation/embedded-vanilla">embed</a> it in your own servers,
but here, I&rsquo;ll illustrate it with the <a href="https://docs.javelit.io/get-started/installation/standalone">standalone command-line tool</a>,
which I&rsquo;ve installed thanks to <a href="https://www.jbang.dev/">Jbang</a>.</p>
<h2 id="it-always-starts-with-_hello-world_">It always starts with <em>&ldquo;Hello World!&rdquo;</em></h2>
<p>A simple example could be:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">/// usr/bin/env jbang &#34;$0&#34; &#34;$@&#34; ; exit $?</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.javelit.core.Jt</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">App</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">main</span>(String<span style="color:#666">[]</span><span style="color:#bbb"> </span>args)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Jt.<span style="color:#4070a0">title</span>(<span style="color:#4070a0">&#34;Hello World!&#34;</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Jt.<span style="color:#4070a0">markdown</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            ## My first official message
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Hello World!
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;&#34;&#34;</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Then, once Javelit is <a href="https://docs.javelit.io/get-started/installation/standalone#install-javelit">installed</a>, you&rsquo;d run it with the following command:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>javelit run App.java
</span></span></code></pre></div><p>It will open your browser automatically, and you&rsquo;ll be able to view the app UI.
Then, start making some changes to the title or markdown text, and notice how the UI is live reloaded.</p>
<p>Later on, add maybe a button, or the <a href="https://docs.javelit.io/develop/api-reference">many components</a> available,
like the various text elements, the input elements &amp; forms, the containers, pages &amp; layouts,
or the data components like tables or charts with <a href="https://echarts.icepear.org/#/">Apache Echarts</a>.</p>
<blockquote>
<p>Have a look at the more complete <a href="https://docs.javelit.io/get-started/installation/standalone#create-a-hello-world-app-and-run-it">Hello World</a>
from the documentation, which shows some interactivity with a button click counter.</p></blockquote>
<h2 id="creating-an-interactive-image-playground-with-nano-banana">Creating an interactive image playground with Nano Banana</h2>
<p>After &ldquo;Hello World!&rdquo;, you&rsquo;ve got to build something a little more involved, right?</p>
<p>Since I love playing with <strong>Nano Banana</strong> (i.e. Gemini 2.5 Flash Image) to create and edit pictures,
I decided to build an <strong>interactive image playground, to create new images,
and then incrementally edit the image with further prompts</strong>.</p>
<p>Here&rsquo;s the UI I came up with, and let&rsquo;s see how to build and interact with it:</p>
<p><figure>
  <a href="#img-ff7d3f7172139cfd8da4005d30d68e75">
    <img src="/img/nano-banana/javelit-nano-banana-playground.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-ff7d3f7172139cfd8da4005d30d68e75">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/nano-banana/javelit-nano-banana-playground.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>What do we see in that UI?
A title, a form containing a text area to enter the prompts, and a button to launch the image generation.
So we&rsquo;ll layout those components in that order:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">App</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">main</span>(String<span style="color:#666">[]</span><span style="color:#bbb"> </span>args)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Jt.<span style="color:#4070a0">title</span>(<span style="color:#4070a0">&#34;🍌 Nano Banana Playground 🍌&#34;</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>form<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">form</span>().<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>text<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">textArea</span>(<span style="color:#4070a0">&#34;Image prompt&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">placeholder</span>(<span style="color:#4070a0">&#34;An impressionist painting of a cat&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">use</span>(form);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(Jt.<span style="color:#4070a0">formSubmitButton</span>(<span style="color:#4070a0">&#34;Generate image&#34;</span>).<span style="color:#4070a0">use</span>(form))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#60a0b0;font-style:italic">// Nano Banana magic to generate or edit the image, then...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>Jt.<span style="color:#4070a0">html</span>(<span style="color:#4070a0">&#34;&lt;img src=&#39;data:&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>mimeType<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#4070a0">&#34;;base64,&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>b64encoded<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#39;&gt;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>.<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>We added:</p>
<ul>
<li>a title with the <code>Jt.title().use()</code> method,</li>
<li>a form, to associate the text area and submit button, with <code>Jt.form().use()</code>,</li>
<li>a text area, with a placeholder, and hosted within the form, with <code>Jt.textArea().use()</code>,</li>
<li>a button to launch the image generation, with <code>Jt.formSubmitButton().use()</code>,</li>
<li>and <code>Jt.html().use()</code> to append an image as a data src <code>img</code> (there&rsquo;s no image component yet, but it&rsquo;s on the <a href="https://github.com/javelit/javelit/discussions/39">roadmap</a>).</li>
</ul>
<p>But what&rsquo;s more interesting is this mysterious <code>if</code> statement&hellip;
The first time, the UI is drawn with the title and form.
But since the user hasn&rsquo;t yet clicked the submit button, the <code>formSubmitButton()</code> method returns <code>false</code>.
So the code inside the <code>if</code> isn&rsquo;t executed.</p>
<p>But once the user interacts with the UI, after having entered some text and clicked on the button,
this time the method will return <code>true</code>, and the image component (here the HTML component) is going to be added to the UI.</p>
<p>At first, this is not really obvious, as you have to think in terms of loop redrawing the UI after each interaction with the components.
But you&rsquo;ll get the hang of it after a little while.</p>
<h2 id="handling-state">Handling state</h2>
<p>The idea of this image playground is to</p>
<ul>
<li>first, create a brand-new image,</li>
<li>but then, you change the prompt with some image editing commands, and each time you submit the form, the image will be updated accordingly.</li>
</ul>
<p>So you need to somehow <a href="https://docs.javelit.io/get-started/fundamentals/advanced-concepts#session-state">keep track of the state</a>
from the previous interaction and rendering loop.
How do you do that? With the <code>Jt.sessionState()</code>.</p>
<p>For example, I want to save the bytes and the mime type of the image generated by Nano Banana, I would do:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Jt.<span style="color:#4070a0">sessionState</span>().<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;mimeType&#34;</span>,<span style="color:#bbb"> </span>mimeType);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Jt.<span style="color:#4070a0">sessionState</span>().<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;bytes&#34;</span>,<span style="color:#bbb"> </span>data);<span style="color:#bbb">
</span></span></span></code></pre></div><p>And if I want to get those variables back in the next rendering loop, I&rsquo;d write:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>String<span style="color:#bbb"> </span>mimeType<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">sessionState</span>().<span style="color:#4070a0">getString</span>(<span style="color:#4070a0">&#34;mimeType&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#902000">byte</span><span style="color:#666">[]</span><span style="color:#bbb"> </span>data<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">sessionState</span>().<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;bytes&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>It&rsquo;s a so-called <em>typed map</em>, and there are many methods like <code>putIfAbsent()</code>, <code>computeInt()</code>, etc.</p>
<h2 id="the-final-version-of-the-code-of-our-playground">The final version of the code of our playground</h2>
<p>Most of the code below is actually generating and editing the image,
as I explained in previous articles <a href="https://glaforge.dev/posts/2025/09/09/calling-nano-banana-from-java/">using the Gemini GenAI Java SDK</a> directly,
or <a href="https://glaforge.dev/posts/2025/09/22/creative-ai-agents-with-adk-and-nano-banana/">within ADK for Java</a>.
The UI code from Javelit is really just a dozen lines or so!</p>

<details>
  <summary>Click to view the whole code</summary>
  <div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">/// usr/bin/env jbang &#34;$0&#34; &#34;$@&#34; ; exit $?</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//DEPS com.google.genai:google-genai:1.24.0</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">package</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">demo</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.genai.Client</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.genai.types.Content</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.genai.types.GenerateContentConfig</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.genai.types.Part</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.javelit.core.Jt</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.Base64</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">App</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">main</span>(String<span style="color:#666">[]</span><span style="color:#bbb"> </span>args)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Jt.<span style="color:#4070a0">title</span>(<span style="color:#4070a0">&#34;🍌 Nano Banana Playground 🍌&#34;</span>).<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>form<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">form</span>().<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>imgContainer<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">empty</span>().<span style="color:#4070a0">use</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>text<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">textArea</span>(<span style="color:#4070a0">&#34;Image prompt&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">placeholder</span>(<span style="color:#4070a0">&#34;An impressionist painting of a cat&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">use</span>(form);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(Jt.<span style="color:#4070a0">formSubmitButton</span>(<span style="color:#4070a0">&#34;Generate image&#34;</span>).<span style="color:#4070a0">use</span>(form))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(Client<span style="color:#bbb"> </span>client<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Client.<span style="color:#4070a0">Builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GOOGLE_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>mimeTypeFromState<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">sessionState</span>().<span style="color:#4070a0">getString</span>(<span style="color:#4070a0">&#34;mimeType&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#902000">byte</span><span style="color:#666">[]</span><span style="color:#bbb"> </span>bytesFromState<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>(<span style="color:#902000">byte</span><span style="color:#666">[]</span>)<span style="color:#bbb"> </span>Jt.<span style="color:#4070a0">sessionState</span>().<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;bytes&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Content<span style="color:#bbb"> </span>content;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// first run --&gt; create a brand-new image</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(mimeTypeFromState<span style="color:#bbb"> </span><span style="color:#666">==</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span><span style="color:#666">||</span><span style="color:#bbb"> </span>bytesFromState<span style="color:#bbb"> </span><span style="color:#666">==</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>content<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Content.<span style="color:#4070a0">fromParts</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span>Part.<span style="color:#4070a0">fromText</span>(text)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span>{<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// second run --&gt; edit the previously generated image</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>content<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Content.<span style="color:#4070a0">fromParts</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span>Part.<span style="color:#4070a0">fromBytes</span>(bytesFromState,<span style="color:#bbb"> </span>mimeTypeFromState),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span>Part.<span style="color:#4070a0">fromText</span>(text)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">models</span>.<span style="color:#4070a0">generateContent</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;gemini-2.5-flash-image-preview&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>content,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>GenerateContentConfig.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">responseModalities</span>(<span style="color:#4070a0">&#34;TEXT&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;IMAGE&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">build</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>response.<span style="color:#4070a0">candidates</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">flatMap</span>(candidates<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>candidates.<span style="color:#4070a0">getFirst</span>().<span style="color:#4070a0">content</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">flatMap</span>(Content::parts)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">flatMap</span>(parts<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>parts.<span style="color:#4070a0">getLast</span>().<span style="color:#4070a0">inlineData</span>()))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">ifPresent</span>(inlineData<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span>String<span style="color:#bbb"> </span>mimeType<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>inlineData.<span style="color:#4070a0">mimeType</span>().<span style="color:#4070a0">orElse</span>(<span style="color:#4070a0">&#34;image/png&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span>inlineData.<span style="color:#4070a0">data</span>().<span style="color:#4070a0">ifPresent</span>(data<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>Jt.<span style="color:#4070a0">sessionState</span>().<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;mimeType&#34;</span>,<span style="color:#bbb"> </span>mimeType);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>Jt.<span style="color:#4070a0">sessionState</span>().<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;bytes&#34;</span>,<span style="color:#bbb"> </span>data);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>String<span style="color:#bbb"> </span>b64encoded<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Base64.<span style="color:#4070a0">getEncoder</span>().<span style="color:#4070a0">encodeToString</span>(data);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>Jt.<span style="color:#4070a0">html</span>(<span style="color:#4070a0">&#34;&lt;img src=&#39;data:&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>mimeType<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#4070a0">&#34;;base64,&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>b64encoded<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#39;&gt;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">use</span>(imgContainer);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span>});<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>});<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div></details>

<h2 id="go-ahead-and-have-fun-with-javelit">Go ahead and have fun with Javelit!</h2>
<p>I had a lot of fun playing with <a href="https://javelit.io/">Javelit</a> so far,
and I&rsquo;m looking forward to using this nice little tool to experiment with various application ideas.
I highly encourage you to try it out, so <a href="https://javelit.io/">go check out Javelit</a>!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Building AI Agents with ADK for Java</title><link>https://glaforge.dev/talks/2025/10/22/building-ai-agents-with-adk-for-java/</link><pubDate>Wed, 22 Oct 2025 12:18:14 +0200</pubDate><guid>https://glaforge.dev/talks/2025/10/22/building-ai-agents-with-adk-for-java/</guid><description>&lt;p>At Devoxx Belgium, I recently had the chance to present this new talk dedicated to
&lt;a href="https://google.github.io/adk-docs/">ADK for Java&lt;/a>,
the open source Agent Development Kit framework developed by Google.&lt;/p>
&lt;p>The presentation covered:&lt;/p>
&lt;ul>
&lt;li>an introduction to the notion of AI agents&lt;/li>
&lt;li>how to &lt;strong>get started in a Java and Maven project&lt;/strong>&lt;/li>
&lt;li>how to create your first agent&lt;/li>
&lt;li>how to debug an agent via the &lt;strong>Dev UI&lt;/strong>&lt;/li>
&lt;li>the coverage of the various &lt;strong>tools&lt;/strong> (custom function tools, built-in tools like Google Search or code execution, an agent as tool, MCP tools)&lt;/li>
&lt;li>an overview of the different ways to combine agents into a &lt;strong>multi-agent system&lt;/strong>: sub-agents, sequential agents, parallel agents, loop agents&lt;/li>
&lt;li>some details on the &lt;strong>event loop&lt;/strong> and services (session and state management, artifacts, runner&amp;hellip;)&lt;/li>
&lt;li>&lt;strong>structured&lt;/strong> input / output schemas&lt;/li>
&lt;li>the various &lt;strong>callbacks&lt;/strong> in the agent lifecycle&lt;/li>
&lt;li>the integration with LangChain4j (to give access to the plethora of LLMs supported by LangChain4j)&lt;/li>
&lt;li>the definition of agents via &lt;strong>configuration in YAML&lt;/strong>&lt;/li>
&lt;li>the new &lt;strong>long-term memory&lt;/strong> support&lt;/li>
&lt;li>the &lt;strong>plugin&lt;/strong> system&lt;/li>
&lt;li>the new external &lt;strong>code executors&lt;/strong> (via Docker containers or backed by Google Cloud Vertex AI)&lt;/li>
&lt;li>how to launch an agent with the Dev UI from &lt;strong>JBang&lt;/strong>&lt;/li>
&lt;/ul>
&lt;h2 id="slides-of-the-presentation">Slides of the presentation&lt;/h2>
&lt;p>The slide deck of this session is embedded below:&lt;/p></description><content:encoded>
<![CDATA[<p>At Devoxx Belgium, I recently had the chance to present this new talk dedicated to
<a href="https://google.github.io/adk-docs/">ADK for Java</a>,
the open source Agent Development Kit framework developed by Google.</p>
<p>The presentation covered:</p>
<ul>
<li>an introduction to the notion of AI agents</li>
<li>how to <strong>get started in a Java and Maven project</strong></li>
<li>how to create your first agent</li>
<li>how to debug an agent via the <strong>Dev UI</strong></li>
<li>the coverage of the various <strong>tools</strong> (custom function tools, built-in tools like Google Search or code execution, an agent as tool, MCP tools)</li>
<li>an overview of the different ways to combine agents into a <strong>multi-agent system</strong>: sub-agents, sequential agents, parallel agents, loop agents</li>
<li>some details on the <strong>event loop</strong> and services (session and state management, artifacts, runner&hellip;)</li>
<li><strong>structured</strong> input / output schemas</li>
<li>the various <strong>callbacks</strong> in the agent lifecycle</li>
<li>the integration with LangChain4j (to give access to the plethora of LLMs supported by LangChain4j)</li>
<li>the definition of agents via <strong>configuration in YAML</strong></li>
<li>the new <strong>long-term memory</strong> support</li>
<li>the <strong>plugin</strong> system</li>
<li>the new external <strong>code executors</strong> (via Docker containers or backed by Google Cloud Vertex AI)</li>
<li>how to launch an agent with the Dev UI from <strong>JBang</strong></li>
</ul>
<h2 id="slides-of-the-presentation">Slides of the presentation</h2>
<p>The slide deck of this session is embedded below:</p>
<script async class="speakerdeck-embed" data-id="1cd42280a265486396c0822b0e0ec716" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<h2 id="video-recording-of-the-talk">Video recording of the talk</h2>
<p>And you can also watch the recoding of this presentation here:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/L6V6aQixOZU?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<h2 id="samples-demonstrated-during-the-conference">Samples demonstrated during the conference</h2>
<p>During the presentation, I demonstrated a lot of samples showing ADK in action.
You can find all the <a href="https://github.com/glaforge/ai-agent-protocols/tree/main/adk/src/main/java/agents/adk">examples</a>
in this <a href="https://github.com/glaforge/ai-agent-protocols/">GitHub repository</a>.
There are also a couple of servers implementing an MCP server and an A2A server agent.</p>
<p>Among those examples, you&rsquo;ll find:</p>
<ul>
<li>a simple <a href="https://github.com/glaforge/ai-agent-protocols/blob/main/adk/src/main/java/agents/adk/_10_ScienceTeacher.java">science teacher</a> agent</li>
<li>the same <a href="https://github.com/glaforge/ai-agent-protocols/blob/main/adk/src/main/java/agents/adk/_12_ScienceTeacher_Live.java">science teacher</a> agent but using voice (with a Gemini Live model)</li>
<li>a <a href="https://github.com/glaforge/ai-agent-protocols/blob/main/adk/src/main/java/agents/adk/_20_StockTicker.java">stock ticker</a> agent that uses a local Java method to look up (fake) stock prices</li>
<li>a <a href="https://github.com/glaforge/ai-agent-protocols/blob/main/adk/src/main/java/agents/adk/_22_LatestNews_Search.java">news search agent</a> configured to use the Google Search built-in tool</li>
<li>an agent that uses the <a href="https://github.com/glaforge/ai-agent-protocols/blob/main/adk/src/main/java/agents/adk/_24_PythonCoder.java">built-in Python code executor tool</a></li>
<li>an agent taking advantage of <a href="https://github.com/glaforge/ai-agent-protocols/blob/main/adk/src/main/java/agents/adk/_26_LocalGuide_Maps.java">Google Maps grounding</a> to find local landmarks or restaurants</li>
<li>a <a href="https://github.com/glaforge/ai-agent-protocols/blob/main/adk/src/main/java/agents/adk/_28_MoonExpert_MCP.java">moon expert</a>! which calls a remote MCP server</li>
<li>a <a href="https://github.com/glaforge/ai-agent-protocols/blob/main/adk/src/main/java/agents/adk/_30_SearchAndTweet_SubAgents.java">multi-agent</a> using the sub-agents approach to search about some topic, and the potentially craft social post messages</li>
<li>a <a href="https://github.com/glaforge/ai-agent-protocols/blob/main/adk/src/main/java/agents/adk/_32_TripPlanner_Sequential.java">trip planner</a> agent using a sequential agent</li>
<li>a <a href="https://github.com/glaforge/ai-agent-protocols/blob/main/adk/src/main/java/agents/adk/_34_CompanyDetective_Parallel.java">company detective</a> agent using the parallel agent approach to launch different searches in parallel</li>
<li>a <a href="https://github.com/glaforge/ai-agent-protocols/blob/main/adk/src/main/java/agents/adk/_36_CodeRefiner_Loop_Exit.java">code refiner</a> agent using the ADK loop agent to loop over different tasks (writing code &amp; critiquing code)</li>
<li>a <a href="https://github.com/glaforge/ai-agent-protocols/blob/main/adk/src/main/java/agents/adk/_40_WeatherForecast_Callback.java">weather forecast</a> agent just to show the various callbacks you can configure on an agent</li>
<li>an agent using a <a href="https://github.com/glaforge/ai-agent-protocols/blob/main/adk/src/main/java/agents/adk/_50_Coffee_LangChain4j.java">local agent running in Ollama</a>, thanks to <a href="https://developers.googleblog.com/en/adk-for-java-opening-up-to-third-party-language-models-via-langchain4j-integration/">my ADK / LangChain4j bridge</a></li>
<li>an example showing how to <a href="https://github.com/glaforge/ai-agent-protocols/blob/main/adk/src/main/java/agents/adk/_60_CapitalCity_YAML.java">load a YAML-defined agent</a></li>
<li>and the bonus for the end: an <a href="https://github.com/glaforge/ai-agent-protocols/blob/main/adk/src/main/java/agents/adk/_70_ImageEditor_NanoBanana.java">agent using the Nano Banana</a> image generation and edition model</li>
</ul>
<h2 id="updated-adk-template-project">Updated ADK template project</h2>
<p>With the recent (somewhat silent) release of <a href="https://github.com/google/adk-java/releases/tag/v0.3.0">version 0.3.0</a>,
I seized the opportunity to also update my <a href="https://github.com/glaforge/adk-java-maven-template">ADK template project</a> on GitHub.
In a <a href="https://glaforge.dev/posts/2025/05/27/adk-java-github-template/">recent article</a>, I wrote about this template project,
how you can clone it or reuse it, to get started with ADK for Java easily, with a sample agent, and a Maven build.</p>
<h2 id="getting-started-with-a-codelab">Getting started with a codelab</h2>
<p>Additionally to the GitHub <a href="https://github.com/glaforge/adk-java-maven-template">project template</a>,
I have developed a <a href="https://goo.gle/codelab-adk-java">codelab to build AI agents with ADK for Java</a>, for Java developers.
This codelab goes through setting up your environment, writing your first agent, empowering an agent with tools, using the built-in Google Search tool,
mastering the various types of workflow patterns (sub-agent, sequential, parallel, loop).</p>
<p><a href="https://goo.gle/codelab-adk-java"><figure>
  <a href="#img-651ae890ec97b2b9c4c4b09b67ab0a7d">
    <img src="/img/adk/adk-java-codelab-screenshot.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-651ae890ec97b2b9c4c4b09b67ab0a7d">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/adk/adk-java-codelab-screenshot.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</a></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Creative Java AI agents with ADK and Nano Banana 🍌</title><link>https://glaforge.dev/posts/2025/09/22/creative-ai-agents-with-adk-and-nano-banana/</link><pubDate>Mon, 22 Sep 2025 16:51:37 +0200</pubDate><guid>https://glaforge.dev/posts/2025/09/22/creative-ai-agents-with-adk-and-nano-banana/</guid><description>&lt;p>Large Language Models (LLMs) are all becoming &lt;em>&amp;ldquo;multimodal&amp;rdquo;&lt;/em>.
They can process text, but also other &lt;em>&amp;ldquo;modalities&amp;rdquo;&lt;/em> in input, like pictures, videos, or audio files.
But models that output more than just text are less common&amp;hellip;&lt;/p>
&lt;p>Recently, I wrote about my &lt;a href="https://glaforge.dev/posts/2025/09/09/calling-nano-banana-from-java/">experiments with &lt;strong>Nano Banana&lt;/strong>&lt;/a> &amp;#x1f34c; (in Java),
a &lt;strong>Gemini chat model flavor that can create and edit images&lt;/strong>.
This is pretty handy in particular for interactive creative tasks, like for example a marketing assistant that would help you design a new product,
by describing it, by futher tweaking its look, by exposing it in different settings for marketing ads, etc.&lt;/p></description><content:encoded>
<![CDATA[<p>Large Language Models (LLMs) are all becoming <em>&ldquo;multimodal&rdquo;</em>.
They can process text, but also other <em>&ldquo;modalities&rdquo;</em> in input, like pictures, videos, or audio files.
But models that output more than just text are less common&hellip;</p>
<p>Recently, I wrote about my <a href="https://glaforge.dev/posts/2025/09/09/calling-nano-banana-from-java/">experiments with <strong>Nano Banana</strong></a> &#x1f34c; (in Java),
a <strong>Gemini chat model flavor that can create and edit images</strong>.
This is pretty handy in particular for interactive creative tasks, like for example a marketing assistant that would help you design a new product,
by describing it, by futher tweaking its look, by exposing it in different settings for marketing ads, etc.</p>
<p>The <em>&ldquo;Nano Banana&rdquo;</em> &#x1f34c; model we&rsquo;ll use today is the nickname for the popular <code>gemini-2.5-flash-image-preview</code> model.
It&rsquo;s not just a <em>conversational</em> AI; it&rsquo;s a creative partner that can generate and edit images right within a chat session.
Not only it generates text, but also images.</p>
<p>And today, we&rsquo;re going to explore how to configure and use this model inside an AI agent developed with
<a href="https://google.github.io/adk-docs/">ADK</a> (<strong>Agent Development Kit</strong>, and especially <a href="https://github.com/google/adk-java">its Java version</a>).
We&rsquo;ll focus on a key piece of the puzzle: processing the image output from the model and saving it for later use.</p>
<h2 id="meet-the-nanobananacreativeagent">Meet the <code>NanoBananaCreativeAgent</code></h2>
<p>Let&rsquo;s look at some code. Our example is the following <code>NanoBananaCreativeAgent</code> class.
Its goal is to act as a creative assistant, using the &#x1f34c; <em>&ldquo;Nano Banana&rdquo;</em> model to handle image-related tasks based on user prompts.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Remark</span>
      </div>
      <div class="admonition-content">
        <p>Maybe in a later post, we&rsquo;ll see how to create a more complete agent like the marketing scenario I suggested in introduction,
but for now, I want to highlight <strong>how to configure the model and save its output</strong> for later use.</p>
      </div>
    </div><p>Here’s how we define the agent using ADK&rsquo;s <code>LlmAgent.builder()</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.adk.agents.BaseAgent</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.adk.agents.LlmAgent</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.genai.types.Content</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.genai.types.Part</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.reactivex.rxjava3.core.Maybe</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.List</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">NanoBananaCreativeAgent</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>BaseAgent<span style="color:#bbb"> </span><span style="color:#06287e">getAgent</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;nano-banana-creative-agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.5-flash-image-preview&#34;</span>)<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// 🍌</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                You are a creative assistant, and you help users
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                create new images or edit existing ones, using the
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                Nano Banana model (aka Gemini 2.5 Flash Image)
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">afterModelCallback</span>((callbackContext,<span style="color:#bbb"> </span>llmResponse)<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#60a0b0;font-style:italic">// We&#39;ll zoom in on this part next!</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>})<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ... main method to run the agent and other helpers</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>There are two important lines here for enabling image generation:</p>
<ol>
<li><code>.model(&quot;gemini-2.5-flash-image-preview&quot;)</code>:
This tells the ADK to route requests to the specific model endpoint capable of generating and editing images.</li>
<li><code>.instruction(...)</code>:
The instruction primes the model, letting it know its role is to be a creative assistant focused on image tasks.</li>
</ol>
<h2 id="handling-the-image-response-with-a-callback">Handling the image response with a callback</h2>
<p>When you ask the model to <em>&ldquo;create an image of a cat wearing a party hat,&rdquo;</em> it doesn&rsquo;t return a URL or a file path.
It returns the image data directly in its response, typically as a <code>Part</code> containing binary data, in a big byte array.</p>

    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>The model usually replies with some text to introduce the image, along with the image.
But sometimes, it can also return text-only, in particular when it asks for clarifications for generating the requested image.
So it&rsquo;s important to check that an image is indeed present in the output of the model.
Also, when the output contains an image, there&rsquo;s only a single one.
It never generates more than one — which means I could use a <code>findFirst()</code> instead of a <code>forEach()</code> in the implementation below.</p>
      </div>
    </div><p>How do we capture and use this binary data?
This is where ADK&rsquo;s <code>afterModelCallback</code> becomes handy.
It&rsquo;s a <strong>hook</strong> that lets you execute custom Java code <strong>immediately after the LLM sends its response</strong>, but before the agent&rsquo;s turn is finished.</p>
<p>Let&rsquo;s look at our callback code:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>.<span style="color:#4070a0">afterModelCallback</span>((callbackContext,<span style="color:#bbb"> </span>llmResponse)<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>llmResponse.<span style="color:#4070a0">content</span>()<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// 1. Let&#39;s find the image part!</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">flatMap</span>(Content::parts)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">stream</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">flatMap</span>(List::stream)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// Filter parts containing image content</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">filter</span>(part<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>part.<span style="color:#4070a0">inlineData</span>().<span style="color:#4070a0">isPresent</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">forEach</span>(part<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#60a0b0;font-style:italic">// 2. Save the image as an artifact for the pipeline</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>callbackContext.<span style="color:#4070a0">saveArtifact</span>(<span style="color:#4070a0">&#34;rendered-image&#34;</span>,<span style="color:#bbb"> </span>part);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#60a0b0;font-style:italic">// 3. Potentially save the image as a file elsewhere</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>Blob<span style="color:#bbb"> </span>blob<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>part.<span style="color:#4070a0">inlineData</span>().<span style="color:#4070a0">get</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#902000">byte</span><span style="color:#666">[]</span><span style="color:#bbb"> </span>imageBytes<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>blob.<span style="color:#4070a0">data</span>().<span style="color:#4070a0">get</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>String<span style="color:#bbb"> </span>mimeType<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>blob.<span style="color:#4070a0">mimeType</span>().<span style="color:#4070a0">get</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>});<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// Returning empty means not altering the agent&#39;s response</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Maybe.<span style="color:#4070a0">empty</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>})<span style="color:#bbb">
</span></span></span></code></pre></div><p>Let&rsquo;s break down what&rsquo;s happening in this lambda:</p>
<ol>
<li>
<p><strong>Find the image part</strong>: We use a Java Stream to navigate the <code>llmResponse</code> structure.
A response can have zero or one <code>Content</code> object (an <code>Optional</code>), with an optional list of <code>Part</code>s (which can be <em>text</em>, <em>function calls</em>, or <em>inline data</em>).
We filter down to find the <code>Part</code> that contains <code>inlineData</code> — this is our image.</p>
</li>
<li>
<p><strong>Save as an artifact</strong>: <code>callbackContext.saveArtifact(&quot;rendered-image&quot;, part)</code> is a key ADK feature.
It saves the raw image <code>Part</code> into the agent&rsquo;s artifact registry under the name <code>rendered-image</code>.
This makes the generated image available to other agents or tools that might run later in a more complex pipeline.</p>
</li>
<li>
<p><strong>Do something with the image bytes</strong>: Potentially, instead of (or in addition to) saving the image as an artifact,
you can decide to do something yourself with the bytes of the image and its MIME type,
like saving that file directly to the file system if you&rsquo;re building some kind of command-line based agent tool.</p>
</li>
</ol>
<h2 id="going-further-building-a-creative-marketing-agent">Going further: building a creative marketing agent</h2>
<p>This simple agent is the building block for a more complex creative workflow.
Imagine a <em>&ldquo;Creative Marketing Agent&rdquo;</em> built as a pipeline of agents.</p>
<ul>
<li>
<p><strong>Step 1 — Product ideation.</strong> A user interacts with our <code>NanoBananaCreativeAgent</code>.
They prompt: <em>&ldquo;Generate an image of a new energy drink can called &lsquo;Cosmic Charge&rsquo;. It should be dark blue with a glowing yellow lightning bolt.&rdquo;</em></p>
</li>
<li>
<p><strong>Step 2 — Image generation &amp; persistence.</strong> Our agent calls the model to generate the image.
The user iterates potentially a few rounds to further improve the rendered picture, thanks to Nano Banana&rsquo;s editing capabilities.
The <code>afterModelCallback</code> we just analyzed fires, and <code>cosmic-charge.png</code> is saved to the file system,
and/or the image is also saved as an artifact in the agent&rsquo;s session.</p>
</li>
<li>
<p><strong>Step 3 — Further asset generation.</strong> A second agent in the pipeline
(via an ADK <code>SequentialAgent</code> that we <a href="https://glaforge.dev/posts/2025/07/24/mastering-agentic-workflows-with-adk-sequential-agent/">explored in a previous article</a>), a <code>MarketingAssetAgent</code>, is triggered.
Its instruction might be: <em>&ldquo;You will be given a product image.
Your job is to create a marketing banner for social media.&rdquo;</em>
This agent can now be given a new prompt like, <em>&ldquo;Take the product image from the &lsquo;rendered-image&rsquo; artifact and place it on a background of a starry nebula.
Add the text &lsquo;Feel the Power of the Cosmos!&rsquo;&rdquo;.</em>
Or you could also have a dedicated video generation agent,
using the <a href="https://gemini.google/overview/video-generation/">Veo 3</a> video model,
to generate a video illustrating the product in action, like someone drinking this fancy energy drink.</p>
</li>
</ul>
<p>By saving the image to a file or as artifact, thanks to the callback trick, we&rsquo;ve successfully passed a complex, generated asset from one stage of our agentic workflow to the next.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Integrating advanced image generation and editing into your Java agentic applications is no longer science-fiction.
With the <strong>Agent Development Kit</strong> for Java, with just a few lines of hook code,
you can configure an agent to use powerful multimodal models like <strong>Nano Banana</strong>.
By leveraging the <code>afterModelCallback</code> in your ADK agent definition, you gain precise control over the model&rsquo;s output,
allowing you to process, save, and chain creative tasks together to build more useful and creative agents.</p>
<p>So go ahead, start experimenting, and see what amazing creative workflows you can build!
And, of course, be sure to read my series of <a href="https://glaforge.dev/tags/agent-development-kit/">articles on ADK for Java</a>!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Creating a Streamable HTTP MCP server with Micronaut</title><link>https://glaforge.dev/posts/2025/09/16/creating-a-streamable-http-mcp-server-with-micronaut/</link><pubDate>Tue, 16 Sep 2025 10:00:31 +0200</pubDate><guid>https://glaforge.dev/posts/2025/09/16/creating-a-streamable-http-mcp-server-with-micronaut/</guid><description>&lt;p>In previous articles, I explored how to
&lt;a href="https://glaforge.dev/posts/2025/05/02/vibe-coding-an-mcp-server-with-micronaut-and-gemini/">create an MCP server with Micronaut&lt;/a>
by &lt;em>vibe-coding&lt;/em> one, following the
&lt;a href="https://modelcontextprotocol.io/docs/getting-started/intro">Model Context Protocol specification&lt;/a>
(which was a great way to better understand the underpinnings) and how to
&lt;a href="https://glaforge.dev/posts/2025/06/09/building-an-mcp-server-with-quarkus-and-deploying-on-google-cloud-run/">create an MCP server with Quarkus&lt;/a>.&lt;/p>
&lt;p>Micronaut lacked a dedicated module for creating MCP servers, but fortunately, recently Micronaut added official support for MCP,
so I was eager to try it out!&lt;/p>
&lt;link rel="stylesheet" href="https://glaforge.dev/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&amp;#43;AD&amp;#43;9EeYXUTm&amp;#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
&lt;div class="admonition note">
&lt;div class="admonition-header">&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">&lt;path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/>&lt;/svg>
&lt;span>For the impatient&lt;/span>
&lt;/div>
&lt;div class="admonition-content">
&lt;p>You can &lt;a href="https://github.com/glaforge/mn-mcp-server">checkout the code&lt;/a> we&amp;rsquo;ll be covering in this article on GitHub.&lt;/p></description><content:encoded>
<![CDATA[<p>In previous articles, I explored how to
<a href="https://glaforge.dev/posts/2025/05/02/vibe-coding-an-mcp-server-with-micronaut-and-gemini/">create an MCP server with Micronaut</a>
by <em>vibe-coding</em> one, following the
<a href="https://modelcontextprotocol.io/docs/getting-started/intro">Model Context Protocol specification</a>
(which was a great way to better understand the underpinnings) and how to
<a href="https://glaforge.dev/posts/2025/06/09/building-an-mcp-server-with-quarkus-and-deploying-on-google-cloud-run/">create an MCP server with Quarkus</a>.</p>
<p>Micronaut lacked a dedicated module for creating MCP servers, but fortunately, recently Micronaut added official support for MCP,
so I was eager to try it out!</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>For the impatient</span>
      </div>
      <div class="admonition-content">
        <p>You can <a href="https://github.com/glaforge/mn-mcp-server">checkout the code</a> we&rsquo;ll be covering in this article on GitHub.</p>
      </div>
    </div><h2 id="what-to-build">What to build?</h2>
<p>Like in my previous article with Quarkus, I decided to build another version of my &#x1f314; moon phases MCP server.
This is interesting to be able to contrast Quarkus and Micronaut&rsquo;s approaches.</p>
<p>I reused my code for calculating the moon phases.
My <code>MoonPhasesService</code> is fairly simple (as long as you don&rsquo;t look at the exact math calculation)
and consists in two methods:</p>
<ul>
<li><code>currentMoonPhase()</code> — to know the phase at this point in time,</li>
<li><code>moonPhaseAtUnixTimestamp(long timeSeconds)</code> — to know the phase at a specific point in time.</li>
</ul>
<p>The contract is as follows, nothing specific to MCP for now:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Singleton</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">MoonPhasesService</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>MoonPhase<span style="color:#bbb"> </span><span style="color:#06287e">currentMoonPhase</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">/*...*/</span><span style="color:#bbb"> </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>MoonPhase<span style="color:#bbb"> </span><span style="color:#06287e">moonPhaseAtUnixTimestamp</span>(<span style="color:#902000">long</span><span style="color:#bbb"> </span>timeSeconds)<span style="color:#bbb"> </span>{<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">/*...*/</span><span style="color:#bbb"> </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Compared to my Quarkus version, the service returns <code>MoonPhase</code> <code>record</code>s instead of <code>enum</code> values,
as it seems Micronaut is unhappy with returning my <code>enum</code>.
So I changed <code>MoonPhase</code> to look like this:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Java" data-lang="Java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@JsonSchema</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#555;font-weight:bold">@Introspected</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">MoonPhase</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@NotBlank</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>phase,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@NotBlank</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>emoji<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb"> </span>{<span style="color:#bbb"> </span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>What&rsquo;s interesting here is the <code>@JsonSchema</code> annotation which comes from the Micronaut JSON Schema module,
which provides very rich support for all the subtleties of the JSON Schema specification.
The <code>@Instrospected</code> annotation is here to help with annotation processing and Ahead-of-Time compilation.</p>
<p>Let&rsquo;s look at the <code>MoonPhasesMcpServer</code> now:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Singleton</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">MoonPhasesMcpServer</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Inject</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span>MoonPhasesService<span style="color:#bbb"> </span>moonPhasesService;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Tool</span>(name<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;current-moon-phase&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Provides the current moon phase&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>MoonPhase<span style="color:#bbb"> </span><span style="color:#06287e">currentMoonPhase</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>moonPhasesService.<span style="color:#4070a0">currentMoonPhase</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Tool</span>(name<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;moon-phase-at-date&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Provides the moon phase at a certain date (with a format of yyyy-MM-dd)&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>MoonPhase<span style="color:#bbb"> </span><span style="color:#06287e">moonPhaseAtDate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@ToolArg</span>(name<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;localDate&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@NotBlank</span><span style="color:#bbb"> </span><span style="color:#555;font-weight:bold">@Pattern</span>(regexp<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;\\d{4}-\\d{2}-\\d{2}&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>localDate<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>LocalDate<span style="color:#bbb"> </span>parsedLocalDate<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LocalDate.<span style="color:#4070a0">parse</span>(localDate);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>moonPhasesService.<span style="color:#4070a0">moonPhaseAtUnixTimestamp</span>(parsedLocalDate.<span style="color:#4070a0">toEpochDay</span>()<span style="color:#bbb"> </span><span style="color:#666">*</span><span style="color:#bbb"> </span>86400);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>You&rsquo;ll find the same couple annotations as in Quarkus: <code>@Tool</code> and <code>@ToolArg</code>.
In Micronaut, <code>@ToolArg</code> is missing a <code>description</code> field, but it should be added soon.</p>
<p>What&rsquo;s more powerful here in Micronaut is the use of Micronaut Validation annotations:
notice the <code>@NotBlank</code> and even better, the <code>@Pattern</code> annotation!</p>
<p>With Micronaut, I don&rsquo;t have to handle the mal-formed inputs, as they are caught by validation much earlier.
If the input is incorrect, Micronaut will handle the situation on its own, and your method won&rsquo;t even be called.
So no need to handle the bad values.</p>
<h2 id="testing-the-mcp-server-with-the-mcp-inspector">Testing the MCP server with the MCP Inspector</h2>
<p>When using MCP Inspector to test my server manually, if I pass a blank value to the <code>moon-phase-at-date</code> method,
I&rsquo;ll see validation kicking in:</p>
<p><figure>
  <a href="#img-9286fc895bcacb87c962507b577f2d42">
    <img src="/img/mcp/mn-mcp-input-error.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-9286fc895bcacb87c962507b577f2d42">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/mcp/mn-mcp-input-error.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<pre tabindex="0"><code>MCP error -32603: moonPhaseAtDate.localDate: must not be blank,
moonPhaseAtDate.localDate: must match &#34;\d{4}-\d{2}-\d{2}&#34;
</code></pre><p>Extra bonus point: Micronaut MCP will create (at compile time) the JSON schemas for the various <code>@JsonSchema</code> annotated beans,
adding more fine-grained information about the manipulated input / output structures.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;$schema&#34;</span>: <span style="color:#4070a0">&#34;https://json-schema.org/draft/2020-12/schema&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;$id&#34;</span>: <span style="color:#4070a0">&#34;http://localhost:8080/schemas/moonPhase.schema.json&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;title&#34;</span>: <span style="color:#4070a0">&#34;Phase of the moon&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;description&#34;</span>: <span style="color:#4070a0">&#34;The phase of the moon is composed of the name of the phase and an emoji representing it&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#4070a0">&#34;object&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;properties&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;emoji&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#4070a0">&#34;string&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;minLength&#34;</span>: <span style="color:#40a070">1</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;phase&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#4070a0">&#34;string&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;minLength&#34;</span>: <span style="color:#40a070">1</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>For those schemas to be served as static assets, <code>application.properties</code> must configure the static resources:</p>
<pre tabindex="0"><code># specify the HTTP Streamable transport
micronaut.mcp.server.transport=HTTP
micronaut.mcp.server.info.name=moon-phases
micronaut.mcp.server.info.version=1.0.0

# Specify how &amp; where the schemas should be exposed
micronaut.router.static-resources.jsonschema.paths=classpath:META-INF/schemas
micronaut.router.static-resources.jsonschema.mapping=/schemas/**

# Potentially define a specific base URL, otherwise it&#39;s infered
# micronaut.jsonschema.validation.baseUri=https://example.com/schemas
</code></pre><h2 id="quick-look-at-the-dependencies">Quick look at the dependencies</h2>
<p>You can checkout the <a href="https://github.com/glaforge/mn-mcp-server">code</a>
and read the <a href="https://github.com/glaforge/mn-mcp-server/blob/main/README.md">README</a>,
but I&rsquo;d like to mention how I scaffolded the project in the first place,
and which dependencies (and tweaks) were needed.</p>
<h3 id="creating-the-micronaut-application">Creating the Micronaut application</h3>
<p>This project was bootstrapped with the <code>mn</code> Micronaut command-line tool,
which can be <a href="https://sdkman.io/sdks/micronaut/">installed via SDKman</a>.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mn create-app --build<span style="color:#666">=</span>gradle --jdk<span style="color:#666">=</span><span style="color:#40a070">21</span> --lang<span style="color:#666">=</span>java --test<span style="color:#666">=</span>junit <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>  --features<span style="color:#666">=</span>jackson-databind,json-schema,validation,json-schema-validation mn.mcp.server.mn-mcp-server
</span></span></code></pre></div><p>As the MCP support is based on the official MCP SDK, which is currently tied to Jackson, you must use the Jackson data binding
(not Micronaut&rsquo;s built-in serialization). You need to add <code>json-schema</code>, <code>validation</code>, and <code>json-schema-validation</code> features.</p>
<p>But you&rsquo;ll have to make some tweaks to the dependencies.</p>
<h3 id="custom-dependency-tweaks">Custom dependency tweaks</h3>
<p>The following dependencies were added to <code>build.gradle</code>, or updated, to support the MCP server and enhance JSON Schema generation:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>dependencies <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// Existing dependencies
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#60a0b0;font-style:italic">// ...
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// The Micronaut MCP support
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    implementation<span style="color:#666">(</span><span style="color:#4070a0">&#34;io.micronaut.mcp:micronaut-mcp-server-java-sdk:0.0.3&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// For rich JSON schema handling
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    annotationProcessor<span style="color:#666">(</span><span style="color:#4070a0">&#34;io.micronaut.jsonschema:micronaut-json-schema-processor:1.7.0&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    implementation<span style="color:#666">(</span><span style="color:#4070a0">&#34;io.micronaut.jsonschema:micronaut-json-schema-annotations:1.7.0&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>First of all, the MCP module is not yet part of the <em>features</em> you can select from the <code>mn</code> command,
or from the <a href="https://micronaut.io/launch/">Micronaut launch site</a>,
but once the MCP support stabilizes, it&rsquo;ll be available.</p>
<p>I had to update the dependency version of the JSON Schema support (instead of using the default version from the BOM),
but this new version will be available soon in the Micronaut BOM.</p>
<p>So maybe when you read this, you&rsquo;ll just add the <code>mcp</code> feature to the list of features, and have everything configured properly.
But those tweaks are just because I&rsquo;m living on the bleeding edge right now!</p>
<h2 id="invoking-the-server-via-gemini-cli">Invoking the server via Gemini CLI</h2>
<p>For the fun, I decided to add this MCP server to my <a href="https://google-gemini.github.io/gemini-cli/">Gemini CLI</a> installation.</p>
<p>Before launching the CLI, I installed the MCP server as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gemini mcp add moonPhases --transport http http://localhost:8080/mcp
</span></span></code></pre></div><p>Then when I launch <code>gemini</code> and list the MCP servers, I can see the moon phase server:</p>
<p><figure>
  <a href="#img-4d3845c5070fb0d285816995c9047e8e">
    <img src="/img/mcp/moon-phase-gemini-cli-1.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-4d3845c5070fb0d285816995c9047e8e">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/mcp/moon-phase-gemini-cli-1.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I ask what was the phase of the moon when mankind first landed on the moon
(and Gemini figures out the correct date format, although I gave it in plain English).
Gemini CLI asks for my acknowledgement to execute the MCP server tool:</p>
<p><figure>
  <a href="#img-96714957a36b740f77cb4b38ebb4f021">
    <img src="/img/mcp/moon-phase-gemini-cli-2.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-96714957a36b740f77cb4b38ebb4f021">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/mcp/moon-phase-gemini-cli-2.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Finally, Gemini CLI responds with a proper English response:</p>
<p><figure>
  <a href="#img-8f6218895a852833c248eb6291aa814b">
    <img src="/img/mcp/moon-phase-gemini-cli-3.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-8f6218895a852833c248eb6291aa814b">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/mcp/moon-phase-gemini-cli-3.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h2 id="going-further">Going further</h2>
<p>I hope you enjoyed the ride so far, but what are the next steps?</p>
<ul>
<li>Check out the <a href="https://github.com/glaforge/mn-mcp-server">source code</a> of this simple HTTP Streamable MCP server.</li>
<li>Read about the <a href="https://micronaut-projects.github.io/micronaut-mcp/latest/guide/#introduction">MCP support in Micronaut</a>.</li>
<li>Have a look at Sergio Delamo&rsquo;s <a href="https://github.com/sdelamo/micronaut-mcp-tools-weather">sample MCP project</a>.</li>
</ul>
<p>Java developers have some great options nowadays for developing their MCP servers, including Quarkus and Micronaut.
Be sure to evaluate those options for your next projects!
For enterprise deployments, nothing beats Java! &#x1f609;
And Micronaut offers a pretty elegant handling of structured inputs and outputs thanks to its rich JSON Schema support.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Generating videos in Java with Veo 3</title><link>https://glaforge.dev/posts/2025/09/10/generating-videos-in-java-with-veo3/</link><pubDate>Wed, 10 Sep 2025 09:46:21 +0200</pubDate><guid>https://glaforge.dev/posts/2025/09/10/generating-videos-in-java-with-veo3/</guid><description>&lt;p>Yesterday, we went bananas &amp;#x1f34c;
&lt;a href="https://glaforge.dev/posts/2025/09/09/calling-nano-banana-from-java/">creating and editing images with Nano Banana, in Java&lt;/a>.
Now, what about &lt;strong>generating videos&lt;/strong> as well, still &lt;strong>in Java&lt;/strong>, with &lt;a href="https://deepmind.google/models/veo/">Veo 3&lt;/a>?&lt;/p>
&lt;p>Especially since this week, Google announced that
&lt;a href="https://developers.googleblog.com/en/veo-3-and-veo-3-fast-new-pricing-new-configurations-and-better-resolution/?utm_campaign=CDR_0x7a40493f_default_b444157129&amp;amp;utm_medium=external&amp;amp;utm_source=blog">Veo 3 became generally available&lt;/a>,
with &lt;strong>reduced pricing&lt;/strong>, a &lt;strong>new 9:16 aspect ratio&lt;/strong> (nice for those vertical viral videos) and even with &lt;strong>resolution up to 1080p&lt;/strong>!&lt;/p>
&lt;p>In today&amp;rsquo;s article, we&amp;rsquo;ll see how to create videos, in Java, with the &lt;a href="https://github.com/googleapis/java-genai">GenAI Java SDK&lt;/a>.
We&amp;rsquo;ll create videos either:&lt;/p></description><content:encoded>
<![CDATA[<p>Yesterday, we went bananas &#x1f34c;
<a href="https://glaforge.dev/posts/2025/09/09/calling-nano-banana-from-java/">creating and editing images with Nano Banana, in Java</a>.
Now, what about <strong>generating videos</strong> as well, still <strong>in Java</strong>, with <a href="https://deepmind.google/models/veo/">Veo 3</a>?</p>
<p>Especially since this week, Google announced that
<a href="https://developers.googleblog.com/en/veo-3-and-veo-3-fast-new-pricing-new-configurations-and-better-resolution/?utm_campaign=CDR_0x7a40493f_default_b444157129&amp;utm_medium=external&amp;utm_source=blog">Veo 3 became generally available</a>,
with <strong>reduced pricing</strong>, a <strong>new 9:16 aspect ratio</strong> (nice for those vertical viral videos) and even with <strong>resolution up to 1080p</strong>!</p>
<p>In today&rsquo;s article, we&rsquo;ll see how to create videos, in Java, with the <a href="https://github.com/googleapis/java-genai">GenAI Java SDK</a>.
We&rsquo;ll create videos either:</p>
<ul>
<li>with a prompt,</li>
<li>or starting with an existing image.</li>
</ul>
<h2 id="setting-up-your-project">Setting up your project</h2>
<p>Depending on your build tool of choice, you&rsquo;ll have to declare the following dependency for the GenAI SDK:</p>
<h3 id="for-maven">For Maven</h3>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>com.google.genai<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>google-genai<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>1.15.0<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div><h3 id="for-gradle">For Gradle</h3>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>dependencies <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    implementation <span style="color:#4070a0">&#34;com.google.genai:google-genai:1.15.0&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Next, you&rsquo;ll need to decide if you wish to use a <a href="https://aistudio.google.com/app/apikey">Google AI API key</a>,
or if you have an existing Google Cloud project, and use it for authentication.</p>
<h3 id="using-a-google-ai-api-key">Using a Google AI API key</h3>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(Client<span style="color:#bbb"> </span>client<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Client.<span style="color:#4070a0">Builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GOOGLE_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><h3 id="using-a-google-cloud-project">Using a Google Cloud project</h3>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(Client<span style="color:#bbb"> </span>client<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Client.<span style="color:#4070a0">Builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GOOGLE_CLOUD_PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GOOGLE_CLOUD_LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">vertexAI</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Now we&rsquo;re ready to go!</p>
<h2 id="choosing-the-right-model">Choosing the right model</h2>
<p>There are two <a href="https://ai.google.dev/gemini-api/docs/video?example=dialogue#model-versions&amp;utm_campaign=CDR_0x7a40493f_default_b444157129&amp;utm_medium=external&amp;utm_source=blog">Veo 3 versions</a>, a super fast one, and a highest quality one:</p>
<ul>
<li><code>veo-3.0-generate-001</code></li>
<li><code>veo-3.0-fast-generate-001</code></li>
</ul>
<p>Let&rsquo;s store the model in a variable:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>String<span style="color:#bbb"> </span>modelName<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;veo-3.0-generate-001&#34;</span>;<span style="color:#bbb">
</span></span></span></code></pre></div>
            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>For the rest of this article, I&rsquo;ll use the best and greatest version!
The generated videos are of higher quality, but they are also more expensive.
So you might want to use the <em>&ldquo;fast&rdquo;</em> variant, if you don&rsquo;t need the highest quality possible, and you also want faster video generation.</p>
      </div>
    </div><p>You can create a video in two ways: from a text prompt, where Veo creates the entire scene, or from an existing image that serves as a starting point, preserving its atmosphere and tone.
Let&rsquo;s see both approaches.</p>
<h2 id="generating-a-video-with-a-prompt">Generating a video with a prompt</h2>
<p>The <code>client.models.generateVideos()</code> method we will use has a few overloads.
I usually prefer the one that accepts a <code>GenerateVideosSource</code> builder object.
On that object, you can configure whether you want to pass a prompt, or an image as starting point.</p>
<p>So let&rsquo;s create a video of a funky banana:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>operation<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">models</span>.<span style="color:#4070a0">generateVideos</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>modelName,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>GenerateVideosSource.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">prompt</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            A funky banana is dancing on the dance floor in a
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            nightclub with flashy color lights and faceted ball,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            and in the background, a neon light says &#34;Veo 3&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>(),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>GenerateVideosConfig.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">aspectRatio</span>(<span style="color:#4070a0">&#34;16:9&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">resolution</span>(<span style="color:#4070a0">&#34;720p&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">generateAudio</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>What does it look (and sound!) like?</p>
<figure class="video">
<video controls preload="metadata" width="800"   >
<source src="/img/veo/video-16-9.mp4" >
</video>
</figure>

    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>Vertex AI and Google AI don&rsquo;t necessarily support the same video configuration options.
The example here was made with Veo from Vertex AI, but with the developer endpoint, for example, you can&rsquo;t specify the resolution or the audio generation parameter.
So be sure to double check which parameters work with the flavor of Veo you&rsquo;re using.</p>
      </div>
    </div><h2 id="generating-a-video-with-an-existing-image">Generating a video with an existing image</h2>
<p>What if instead, I already created an image that I want to animate.
For example, I created this nice dancing banana with Nano Banana
(check yesterday&rsquo;s post on <a href="https://glaforge.dev/posts/2025/09/09/calling-nano-banana-from-java/">Nano Banana from Java</a>):</p>
<p><figure>
  <a href="#img-a40e8a878a20eaf393c08da8e0c9ae89">
    <img src="/img/veo/banana.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-a40e8a878a20eaf393c08da8e0c9ae89">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/veo/banana.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>So let&rsquo;s animate <em>that</em> particular image (<code>banana.png</code>) and this time, I&rsquo;ll use a 9:16 vertical format, so that I can easily share it on social media, with a nice aspect ratio for mobile devices:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>operation<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">models</span>.<span style="color:#4070a0">generateVideos</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>modelName,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>GenerateVideosSource.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">prompt</span>(<span style="color:#4070a0">&#34;The banana is dancing to the sound of disco music&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">image</span>(Image.<span style="color:#4070a0">fromFile</span>(<span style="color:#4070a0">&#34;banana.png&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;image/png&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>(),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>GenerateVideosConfig.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">aspectRatio</span>(<span style="color:#4070a0">&#34;9:16&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">resolution</span>(<span style="color:#4070a0">&#34;1080p&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">generateAudio</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Here&rsquo;s the resulting video:</p>
<figure class="video">
<video controls preload="metadata" width="600"   >
<source src="/img/veo/video-banana-9-16.mp4" >
</video>
</figure>
<p>Do you feel like dancing now? Or maybe you&rsquo;d rather eat a banana?</p>
<h2 id="waiting-for-the-video-to-be-generated">Waiting for the video to be generated</h2>
<p>I showed the videos in the previous sections already, but I didn&rsquo;t yet explain how to retrieve those generated videos.</p>
<p>Whereas images are quite fast to generate (so we can wait synchronously for them) videos on the other hand take longer to generate (between 1 and 2 minutes).
That&rsquo;s why we&rsquo;re going to have to <em>poll</em> to check their status.</p>
<p>The <code>generateVideos()</code> method returns a <code>GenerateVideosOperation</code> object, which is a snapshot of the status of the operation.
We have to request a new <code>GenerateVideosOperation</code> instance each time we want to check the status:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">while</span><span style="color:#bbb"> </span>(<span style="color:#666">!</span>operation.<span style="color:#4070a0">done</span>().<span style="color:#4070a0">orElse</span>(<span style="color:#007020;font-weight:bold">false</span>))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Thread.<span style="color:#4070a0">sleep</span>(1000);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>operation<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">operations</span>.<span style="color:#4070a0">getVideosOperation</span>(operation,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>With a <code>while</code> loop, I&rsquo;m checking every second when/if the video is finished or not.
And I request a new operation object and check again.</p>
<p>When the operation is actually <em>done</em>, we can fetch the video bytes and save them in a file.
There&rsquo;s a bit of Java stream magic involved here, because the API is (unfortunately) relying too much on <code>Optional</code>s for everything:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>operation.<span style="color:#4070a0">response</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">flatMap</span>(GenerateVideosResponse::generatedVideos)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">stream</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">flatMap</span>(List::stream)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">findFirst</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">ifPresent</span>(video<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">files</span>.<span style="color:#4070a0">download</span>(video,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;video.mp4&#34;</span>,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>));<span style="color:#bbb">
</span></span></span></code></pre></div><p>And here you go, the video is now available and saved in the <code>video.mp4</code> file.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In this article, we&rsquo;ve seen <strong>how to use the GenAI Java SDK to generate videos with Veo 3 in Java</strong>.
We explored creating videos from both text prompts and existing images, configuring aspect ratios and resolutions,
and handling the asynchronous nature of video generation by polling for results. With <strong>just a few lines of Java code</strong>,
you can now integrate powerful video generation capabilities into your applications.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Generating and editing images with Nano Banana in Java</title><link>https://glaforge.dev/posts/2025/09/09/calling-nano-banana-from-java/</link><pubDate>Tue, 09 Sep 2025 07:20:21 +0200</pubDate><guid>https://glaforge.dev/posts/2025/09/09/calling-nano-banana-from-java/</guid><description>&lt;p>By now, you&amp;rsquo;ve all probably seen the incredible images generated by the &lt;a href="https://blog.google/products/gemini/updated-image-editing-model/">&lt;strong>Nano Banana&lt;/strong>&lt;/a> model
(also known as Gemini 2.5 Flash Image &lt;em>preview&lt;/em>)?
If you haven&amp;rsquo;t, I encourage you to play with it within &lt;a href="https://aistudio.google.com/prompts/new_chat?model=gemini-2.5-flash-image-preview">Google AI Studio&lt;/a>,
and from the &lt;a href="https://gemini.google.com/">Gemini app&lt;/a>.
or have a look at the &lt;a href="https://x.com/NanoBanana/with_replies">@NanoBanana X/Twitter account&lt;/a> which shares some of its greatest creations.&lt;/p>
&lt;p>&lt;strong>As a Java developer&lt;/strong>, you may be wondering how you can &lt;strong>integrate Nano Banana&lt;/strong> in your &lt;em>own&lt;/em> LLM-powered apps.
This is what this article is about! I&amp;rsquo;ll show you how you can use this model to:&lt;/p></description><content:encoded>
<![CDATA[<p>By now, you&rsquo;ve all probably seen the incredible images generated by the <a href="https://blog.google/products/gemini/updated-image-editing-model/"><strong>Nano Banana</strong></a> model
(also known as Gemini 2.5 Flash Image <em>preview</em>)?
If you haven&rsquo;t, I encourage you to play with it within <a href="https://aistudio.google.com/prompts/new_chat?model=gemini-2.5-flash-image-preview">Google AI Studio</a>,
and from the <a href="https://gemini.google.com/">Gemini app</a>.
or have a look at the <a href="https://x.com/NanoBanana/with_replies">@NanoBanana X/Twitter account</a> which shares some of its greatest creations.</p>
<p><strong>As a Java developer</strong>, you may be wondering how you can <strong>integrate Nano Banana</strong> in your <em>own</em> LLM-powered apps.
This is what this article is about! I&rsquo;ll show you how you can use this model to:</p>
<ul>
<li>create new images</li>
<li>edit existing images</li>
<li>assemble images together</li>
</ul>
<p>For that, I&rsquo;ll be using the <a href="https://github.com/googleapis/java-genai">GenAI Java SDK</a> from Google.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Remark</span>
      </div>
      <div class="admonition-content">
        <p>On this blog, I regularly talk about <a href="https://docs.langchain4j.dev/">LangChain4j</a> and <a href="https://github.com/google/adk-java">ADK for Java</a>.
It&rsquo;s possible to use Nano Banana in ADK, but it&rsquo;s not yet possible to use it with LangChain4j,
because LangChain4j doesn&rsquo;t yet support models that feature output multimodality (i.e. returning text and images).
I&rsquo;ll definitely come back to that once it&rsquo;s supported to show how to call Nano Banana from LangChain4j.</p>
      </div>
    </div><h2 id="creating-your-first-image">Creating your first image</h2>
<p>First of all, you&rsquo;ll need to add the following dependency:</p>
<h3 id="for-maven">For Maven</h3>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>com.google.genai<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>google-genai<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>1.15.0<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div><h3 id="for-gradle">For Gradle</h3>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>dependencies<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>implementation<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;com.google.genai:google-genai:1.15.0&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Next, you&rsquo;ll need to decide if you wish to use a <a href="https://aistudio.google.com/app/apikey">Google AI API key</a>,
or if you have an existing Google Cloud project, and use it for authentication.</p>
<h3 id="using-a-google-ai-api-key">Using a Google AI API key</h3>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(Client<span style="color:#bbb"> </span>client<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Client.<span style="color:#4070a0">Builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GOOGLE_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><h3 id="using-a-google-cloud-project">Using a Google Cloud project</h3>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(Client<span style="color:#bbb"> </span>client<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Client.<span style="color:#4070a0">Builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GOOGLE_CLOUD_PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GOOGLE_CLOUD_LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">vertexAI</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Time to generate our first image!</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">models</span>.<span style="color:#4070a0">generateContent</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;gemini-2.5-flash-image-preview&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        An impressionist oil painting
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        of the port of La Rochelle
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        with its towers and sailing ships.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>GenerateContentConfig.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">responseModalities</span>(<span style="color:#4070a0">&#34;TEXT&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;IMAGE&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>());<span style="color:#bbb">
</span></span></span></code></pre></div><p>Call the <code>generateContent()</code> method with the name of the model, the text prompt, and some extra configuration to specify that we want both text and image output.</p>
<blockquote>
<p><strong>Note:</strong> When using Nano Banana with Google Cloud Vertex AI, this <code>GenerateContentConfig</code> setting is required, but it&rsquo;s implicit when using the Google AI API endpoint instead.
It&rsquo;s a little discrepancy that might be ironed out later on, but in the meantime, always specify that parameter so that your code works for both flavors of the model and API endpoint.</p></blockquote>
<p>Then you need to save the image part of the response into a file, or potentially stream it to the user.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(Part<span style="color:#bbb"> </span>part<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>Objects.<span style="color:#4070a0">requireNonNull</span>(response.<span style="color:#4070a0">parts</span>()))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(part.<span style="color:#4070a0">inlineData</span>().<span style="color:#4070a0">isPresent</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>blob<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>part.<span style="color:#4070a0">inlineData</span>().<span style="color:#4070a0">get</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(blob.<span style="color:#4070a0">data</span>().<span style="color:#4070a0">isPresent</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>Files.<span style="color:#4070a0">write</span>(Paths.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;oil.png&#34;</span>),<span style="color:#bbb"> </span>blob.<span style="color:#4070a0">data</span>().<span style="color:#4070a0">get</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">break</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">catch</span><span style="color:#bbb"> </span>(IOException<span style="color:#bbb"> </span>e)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">throw</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>RuntimeException(e);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><blockquote>
<p><strong>Important:</strong> Nano Banana is actually a chat model!
So it can return both text and image.
It may sometimes ask follow up questions to be sure what image it should generate, so an image is not necessarily always present in the output.
But when an image is generated, there&rsquo;s always just one.</p></blockquote>
<p>Let&rsquo;s see what my oil painting looks like:</p>
<p><figure>
  <a href="#img-8982c83d900105a70cdba099b7b77f49">
    <img src="/img/nano-banana/oil.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-8982c83d900105a70cdba099b7b77f49">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/nano-banana/oil.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>It looks very much like an oil painting from an impressionist painter!</p>
<h2 id="editing-an-existing-image">Editing an existing image</h2>
<p>An area where Nano Banana excels, it&rsquo;s for editing an existing image.
That&rsquo;s why some people claim it&rsquo;s a Photoshop killer!</p>
<p>To edit an image, you&rsquo;ll have to use a variant of the <code>generateContent()</code> method that takes a <code>Content</code> object (made of <code>Part</code>s),
instead of using the method that takes a simple string, because you&rsquo;ll need to pass:</p>
<ul>
<li>the <strong>image</strong> you want to edit,</li>
<li>as well as the <strong>instructions</strong> of the changes you want to make.</li>
</ul>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">models</span>.<span style="color:#4070a0">generateContent</span>(modelName,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Content.<span style="color:#4070a0">fromParts</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Part.<span style="color:#4070a0">fromBytes</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>Files.<span style="color:#4070a0">readAllBytes</span>(Path.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;oil.png&#34;</span>)),<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;image/png&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Part.<span style="color:#4070a0">fromText</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Simplify this painting to focus on key elements, turn
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            this oil painting into a black and white ink noir comic
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            drawing, make the weather rainy and change the time of
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            the day to be at night.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>GenerateContentConfig.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">responseModalities</span>(<span style="color:#4070a0">&#34;TEXT&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;IMAGE&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>());<span style="color:#bbb">
</span></span></span></code></pre></div><p>Here, I pass the <code>oil.png</code> image that we created in the previous run, and add the instructions to change that oil painting into a noir comic style, on a rainy night.
You should also update the file name in which to save the content of the image (I changed it to <code>noir.png</code> in my code).
Let&rsquo;s see if it looks like some noir comic style:</p>
<p><figure>
  <a href="#img-48a0337be8cb6ef352723dc803ec5571">
    <img src="/img/nano-banana/noir.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-48a0337be8cb6ef352723dc803ec5571">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/nano-banana/noir.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Spot on!</p>
<h2 id="combining-several-images">Combining several images</h2>
<p>Another area where Nano Banana is extremely good at, it&rsquo;s for combining several images together.
This capability is very important for example for product marketers who want to maybe put a bottle of perfume in a different decor,
or to make a person do a virtual try on of some new clothes.</p>
<p>Let&rsquo;s say I want to put this person, in the decor, and make her wear the red dress:</p>
<p><figure>
  <a href="#img-e17818491565d38e8c6379b92d54bf9a">
    <img src="/img/nano-banana/images-to-combine.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-e17818491565d38e8c6379b92d54bf9a">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/nano-banana/images-to-combine.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Let&rsquo;s tweak the <code>generateContent()</code> call again, passing all three images, and the editing instructions:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>client.<span style="color:#4070a0">models</span>.<span style="color:#4070a0">generateContent</span>(modelName,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Content.<span style="color:#4070a0">fromParts</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Part.<span style="color:#4070a0">fromBytes</span>(Files.<span style="color:#4070a0">readAllBytes</span>(Path.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;decor.jpg&#34;</span>)),<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;image/jpeg&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Part.<span style="color:#4070a0">fromBytes</span>(Files.<span style="color:#4070a0">readAllBytes</span>(Path.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;taylor.jpg&#34;</span>)),<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;image/jpeg&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Part.<span style="color:#4070a0">fromBytes</span>(Files.<span style="color:#4070a0">readAllBytes</span>(Path.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;red-dress.png&#34;</span>)),<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;image/png&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Part.<span style="color:#4070a0">fromText</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Add this person to the exterior decor,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            and make her wear the red dress.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>GenerateContentConfig.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">responseModalities</span>(<span style="color:#4070a0">&#34;TEXT&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;IMAGE&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>());<span style="color:#bbb">
</span></span></span></code></pre></div><p>The result speaks for itself:</p>
<p><figure>
  <a href="#img-0b148b8da331fd702855363233f9505c">
    <img src="/img/nano-banana/combined.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-0b148b8da331fd702855363233f9505c">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/nano-banana/combined.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Next time, maybe I&rsquo;ll use better lighting!</p>
<h2 id="conclusion-and-going-further">Conclusion and going further</h2>
<p>I hope you enjoyed this quick tutorial on <strong>how to generate and edit images with Nano Banana in Java</strong>!
It&rsquo;s a very fun model to use, so don&rsquo;t hesitate to unleash your creativity.
And again, you don&rsquo;t need to be a &#x1f40d; Python developer to do that!
<strong>You can do everything in Java</strong> &#x2615;</p>
<p>In the meantime, have fun generating cool images with Nano Banana! In particular from Java!
And don&rsquo;t hesitate to share them with me on social media (links at the bottom of this blog),
I&rsquo;m curious to see your creations!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>In-browser semantic search with EmbeddingGemma</title><link>https://glaforge.dev/posts/2025/09/08/in-browser-semantic-search-with-embeddinggemma/</link><pubDate>Mon, 08 Sep 2025 09:04:29 +0200</pubDate><guid>https://glaforge.dev/posts/2025/09/08/in-browser-semantic-search-with-embeddinggemma/</guid><description>&lt;p>A few days ago, Google DeepMind released a new embedding model based on the Gemma open weight model: &lt;a href="https://developers.googleblog.com/en/introducing-embeddinggemma/?utm_campaign=CDR_0x7a40493f_default_b443675976&amp;amp;utm_medium=external&amp;amp;utm_source=blog">EmbeddingGemma&lt;/a>.
With &lt;strong>308 million parameters&lt;/strong>, such a model is tiny enough to be able to &lt;strong>run on edge devices&lt;/strong> like your phone, tablet, or your computer.&lt;/p>
&lt;p>Embedding models are the cornerstone of &lt;a href="https://glaforge.dev/tags/retrieval-augmented-generation">Retrieval Augmented Generation&lt;/a> systems (RAG),
and what generally powers &lt;strong>semantic search&lt;/strong> solutions.
Being able to run an embedding model &lt;em>locally&lt;/em> means you don&amp;rsquo;t need to rely on a server (no need to send your data over the internet): this is great for privacy.
And of course, cost is reduced as well, because you don&amp;rsquo;t need to pay for a remote / hosted embedding model.&lt;/p></description><content:encoded>
<![CDATA[<p>A few days ago, Google DeepMind released a new embedding model based on the Gemma open weight model: <a href="https://developers.googleblog.com/en/introducing-embeddinggemma/?utm_campaign=CDR_0x7a40493f_default_b443675976&amp;utm_medium=external&amp;utm_source=blog">EmbeddingGemma</a>.
With <strong>308 million parameters</strong>, such a model is tiny enough to be able to <strong>run on edge devices</strong> like your phone, tablet, or your computer.</p>
<p>Embedding models are the cornerstone of <a href="/tags/retrieval-augmented-generation">Retrieval Augmented Generation</a> systems (RAG),
and what generally powers <strong>semantic search</strong> solutions.
Being able to run an embedding model <em>locally</em> means you don&rsquo;t need to rely on a server (no need to send your data over the internet): this is great for privacy.
And of course, cost is reduced as well, because you don&rsquo;t need to pay for a remote / hosted embedding model.</p>
<p>In this article, I&rsquo;ll walk you through how I built a simple semantic search application.
This web app allows users to add a collection of documents, type a query, and instantly get a ranked list of the most relevant documents based on their semantic similarity.</p>
<p><figure>
  <a href="#img-a4371863e283683bef99584076060cbf">
    <img src="/img/embedding-gemma/embedding-gemma-semantic-search.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-a4371863e283683bef99584076060cbf">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/embedding-gemma/embedding-gemma-semantic-search.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I&rsquo;ll show you how I brought this to life using the following stack:</p>
<ul>
<li><strong>The embedding model:</strong> Google&rsquo;s new, lightweight <strong>EmbeddingGemma</strong> model.</li>
<li><strong>The inference engine:</strong> 🤗 HuggingFace&rsquo;s <strong>Transformers.js</strong> library, which runs the model directly in the browser.</li>
<li><strong>The UI:</strong> A simple and clean interface built with <strong>Vite, React, and Tailwind CSS</strong>.</li>
<li><strong>The deployment:</strong> A fully automated CI/CD pipeline using <strong>GitHub Actions</strong> to deploy the static site to 🔥 <strong>Firebase Hosting</strong>.</li>
</ul>
<p>Ready to see how it&rsquo;s done? Let&rsquo;s dive in.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition tip">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M272 384c9.6-31.9 29.5-59.1 49.2-86.2c0 0 0 0 0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4c0 0 0 0 0 0c19.8 27.1 39.7 54.4 49.2 86.2l160 0zM192 512c44.2 0 80-35.8 80-80l0-16-160 0 0 16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z"/></svg>
        <span>Tip</span>
      </div>
      <div class="admonition-content">
        <p>For those who are in a hurry, feel free to check out the live demo or browse the source code on GitHub:</p>
<ul>
<li><strong>Live Demo:</strong> <a href="https://embedding-gemma.web.app/">https://embedding-gemma.web.app/</a></li>
<li><strong>GitHub Repository:</strong> <a href="https://github.com/glaforge/embedding-gemma-semantic-search">https://github.com/glaforge/embedding-gemma-semantic-search</a></li>
</ul>
<p>For the demo, 1️⃣ first click to load the weights of the model, 2️⃣ then add a few documents in the <em>database</em>, 3️⃣ finally you can ask a question, 4️⃣ and find the most relevant documents.</p>
      </div>
    </div><h2 id="why-run-ai-in-the-browser">Why run AI in the browser?</h2>
<p>Running an AI model (here an embedding model) directly on the client-side might seem unconventional considering the best models are usually too big to run on edge devices, but it offers a compelling set of advantages, especially for applications like this one:</p>
<ul>
<li><strong>Privacy:</strong> Since all the data processing and embedding calculations happen on the user&rsquo;s device, no sensitive information ever leaves the browser. The documents and queries are never sent over the network, making it a perfect solution for applications that handle personal or confidential text.</li>
<li><strong>Zero added server costs:</strong> The <em>&ldquo;backend&rdquo;</em> is the user&rsquo;s browser. No need for an expensive GPU-powered servers to run the AI model. The application itself is just a set of static files, which can be hosted for free on services like 🔥 Firebase Hosting or GitHub Pages. Of course, the <em>rest</em> of your application may need servers, but at least this part isn&rsquo;t tied to a server.</li>
<li><strong>Low latency:</strong> With the model running locally, there&rsquo;s no network round-trip to a server. Once the model weights are loaded in memory, search queries are processed instantly, providing a snappy and responsive user experience. Well, at least as long as you don&rsquo;t have many millions of documents to search through, as it&rsquo;ll scale linearly without a proper vector database.</li>
<li><strong>Offline-first capability:</strong> After the initial load, the entire application and the AI model can be cached by the browser (and the data be stored in the browser&rsquo;s database or local storage), allowing it to function perfectly even without an internet connection.</li>
</ul>
<h2 id="the-core-components-a-model-and-a-library">The core components: a model and a library</h2>
<p>At the heart of my application are two key pieces of technology that make in-browser semantic search possible:</p>
<h3 id="the-model-embeddinggemma">The model: <code>EmbeddingGemma</code></h3>
<p>The &ldquo;brain&rdquo; of the search is <strong>EmbeddingGemma</strong>, the new, state-of-the-art (SOTA) text embedding model from Google. Unlike massive language models designed for generating text, embedding models are specialized for a different task: converting a piece of text into a numerical vector (a list of numbers). This vector represents the text&rsquo;s semantic meaning. <strong>The closer two vectors are to each other in mathematical space, the more similar their meanings are.</strong></p>
<p>EmbeddingGemma is the perfect choice for this project for several reasons:</p>
<ul>
<li><strong>High performance, small size:</strong> It is the highest-performing model of its size (under 500M parameters) on the multilingual <a href="https://huggingface.co/spaces/mteB/leaderboard">Massive Text Embedding Benchmark (MTEB)</a>. Built on the <a href="https://deepmind.google/models/gemma/gemma-3/">Gemma 3</a> architecture, it&rsquo;s designed for on-device applications where resources are limited.</li>
<li><strong>On-device efficiency:</strong> With quantization, the model&rsquo;s memory footprint can be less than 200MB, making it ideal for running in a web browser without overwhelming the user&rsquo;s device.</li>
<li><strong>Matryoshka Representation Learning (MRL):</strong> While the model produces a high-quality, full-size embedding with 768 dimensions, <a href="https://huggingface.co/blog/matryoshka">MRL</a> allows us to truncate that vector to a smaller size (512, 256, or 128 dimensions) with a minimal loss in accuracy. This gives us a good trade-off between performance and computational cost. In my application, I use the first 128 dimensions for the vector visualizations, which is a perfect example of MRL in action.</li>
<li><strong>Multilingual support:</strong> The model was trained on data from over 100 languages. This is quite rare for an embedding model of that size to be good across a wide variety of spoken languages.</li>
</ul>
<h3 id="the-library-transformersjs">The library: <code>Transformers.js</code></h3>
<p>The &ldquo;engine&rdquo; that runs the model is <a href="https://huggingface.co/docs/transformers.js/index">Transformers.js</a> from 🤗 HuggingFace. This cool JavaScript library is designed to run a wide variety of popular AI models directly in the browser. It handles all the complex, low-level work of loading the model, managing the cache, and executing the computations efficiently using the browser&rsquo;s and device&rsquo;s capabilities.</p>
<p>Transformers.js make it simple to run a model like EmbeddingGemma on the client-side. With it, as shown in 🤗 HuggingFace&rsquo;s <a href="https://huggingface.co/blog/embeddinggemma">blog post</a>; it only takes a few lines of code to get a model up and running, as we&rsquo;ll see in the next section.</p>
<h2 id="how-the-code-works">How the code works</h2>
<p>While the UI is a standard React application built with Vite and styled with Tailwind CSS, the most interesting part is the <code>embeddingService.ts</code> file, which acts as a wrapper around the Transformers.js library.</p>
<blockquote>
<p>I actually vibe-coded the whole application thanks to <a href="https://ai.dev/">Google AI Studio</a> and <a href="https://github.com/google-gemini/gemini-cli">Gemini CLI</a>.
As I&rsquo;m not an expert in React or TypeScript, that was easier to guide Gemini to make it create the UI I wanted, and I fed Gemini also the code in the 🤗 HuggingFace article to get started with the inference.</p></blockquote>
<p>Let&rsquo;s look at a simplified version of the core logic.</p>
<h3 id="initializing-the-model">Initializing the model</h3>
<p>First, I needed to create a singleton instance of the service. This ensures I only ever initialize one copy of the model. The <code>getInstance</code> method handles this, and the <code>init</code> method does the heavy lifting, by calling the <code>AutoTokenizer.from_pretrained()</code> and <code>AutoModel.from_pretrained()</code> method, for loading the text tokenizer and the model respectively.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> { AutoTokenizer, AutoModel } <span style="color:#007020;font-weight:bold">from</span> <span style="color:#4070a0">&#34;@huggingface/transformers&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> EmbeddingService {
</span></span><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">private</span> tokenizer: <span style="color:#902000">AutoTokenizer</span> <span style="color:#666">|</span> <span style="color:#007020;font-weight:bold">null</span> <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">null</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">private</span> model: <span style="color:#902000">AutoModel</span> <span style="color:#666">|</span> <span style="color:#007020;font-weight:bold">null</span> <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">null</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic">// ... singleton logic ...
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>
</span></span><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">private</span> <span style="color:#007020;font-weight:bold">async</span> init() {
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// Load the tokenizer and model from the /model/ directory
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#007020;font-weight:bold">this</span>.tokenizer <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">await</span> AutoTokenizer.from_pretrained(<span style="color:#4070a0">&#34;/model/&#34;</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">this</span>.model <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">await</span> AutoModel.from_pretrained(<span style="color:#4070a0">&#34;/model/&#34;</span>, {
</span></span><span style="display:flex;"><span>      dtype<span style="color:#666">:</span> <span style="color:#4070a0">&#34;q4&#34;</span>, <span style="color:#60a0b0;font-style:italic">// Use a quantized version for efficiency
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    });
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div>
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>The model can also be loaded from 🤗 HuggingFace&rsquo;s Hub, but I wanted the model to be local as well, for a faster loading experience and for a full local-first approach.</p>
      </div>
    </div><h3 id="generating-embeddings-and-calculating-similarity">Generating embeddings and calculating similarity</h3>
<p>Once the model is ready, it can be fed the query and documents. The model expects specific prefixes for queries and documents to perform best (<code>task: search result | query:</code> and <code>title: none | text:</code>), so I made sure to add those first.</p>
<p>The core steps are:</p>
<ol>
<li><strong>Tokenize:</strong> Convert the text (query and documents) into tokens that the model can understand.</li>
<li><strong>Embed:</strong> Pass the tokens to the model to get the sentence embeddings.</li>
<li><strong>Calculate similarity:</strong> Use a matrix multiplication (<code>matmul</code>) of the embeddings with their transpose to get a similarity score between the query and every document.</li>
</ol>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// ... inside the EmbeddingService class ...
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">async</span> embed(query: <span style="color:#902000">string</span>, documents: <span style="color:#902000">string</span>[]) {
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic">// ...
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>  <span style="color:#60a0b0;font-style:italic">// Add the required prefixes for the model
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>  <span style="color:#007020;font-weight:bold">const</span> prefixedQuery <span style="color:#666">=</span> <span style="color:#4070a0">&#34;task: search result | query: &#34;</span> <span style="color:#666">+</span> query;
</span></span><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">const</span> prefixedDocs <span style="color:#666">=</span> documents.map(doc <span style="color:#666">=&gt;</span> <span style="color:#4070a0">&#34;title: none | text: &#34;</span> <span style="color:#666">+</span> doc);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic">// 1. Tokenize the inputs
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>  <span style="color:#007020;font-weight:bold">const</span> inputs <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">await</span> <span style="color:#007020;font-weight:bold">this</span>.tokenizer([prefixedQuery, ...prefixedDocs], {
</span></span><span style="display:flex;"><span>    padding: <span style="color:#902000">true</span>,
</span></span><span style="display:flex;"><span>    truncation: <span style="color:#902000">true</span>,
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic">// 2. Get the sentence embeddings
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>  <span style="color:#007020;font-weight:bold">const</span> { sentence_embedding } <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">await</span> <span style="color:#007020;font-weight:bold">this</span>.model(inputs);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic">// 3. Calculate the similarity scores
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>  <span style="color:#007020;font-weight:bold">const</span> scores <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">await</span> matmul(sentence_embedding, sentence_embedding.transpose(<span style="color:#40a070">1</span>, <span style="color:#40a070">0</span>));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic">// The first row of the scores matrix contains the similarity
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>  <span style="color:#60a0b0;font-style:italic">// of the query to all other documents.
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>  <span style="color:#007020;font-weight:bold">const</span> similarities <span style="color:#666">=</span> (scores.tolist() <span style="color:#007020;font-weight:bold">as</span> <span style="color:#902000">number</span>[][])[<span style="color:#40a070">0</span>].slice(<span style="color:#40a070">1</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#60a0b0;font-style:italic">// ... logic to rank documents based on scores ...
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>}
</span></span></code></pre></div><p>And that&rsquo;s the core of it! With just these two methods (<code>init()</code> and <code>embed()</code>), I had a fully functional semantic search engine running in the browser.</p>
<h2 id="visualizing-the-embeddings">Visualizing the embeddings</h2>
<p>To make the concept of semantic similarity more tangible, I added a simple visualization for each document&rsquo;s embedding vector and the search query.
As soon as you type a few characters in the search query input field, or when you add a new document, you&rsquo;ll see a colored representation of its vector.</p>
<p><figure>
  <a href="#img-1fd30944dd89407a2753e3adf47fff2c">
    <img src="/img/embedding-gemma/embedding-gemma-visualization.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-1fd30944dd89407a2753e3adf47fff2c">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/embedding-gemma/embedding-gemma-visualization.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Each cell in the visualization represents one of the numbers in the embedding vector, with its color intensity indicating the value. When you compare the visualizations of the query and a relevant document, you can often spot visual similarities in their patterns, offering an intuitive glimpse into how the model &ldquo;sees&rdquo; the relationships between texts.</p>
<p>This is where the <a href="https://arxiv.org/abs/2205.13147">Matryoshka Representation Learning</a> (MRL) feature of EmbeddingGemma truly shines. The full embedding vector has 768 dimensions, which would be too much to display effectively. Thanks to MRL, I can use just the first 128 dimensions of the vector for this visualization with a minimal loss of semantic information. This provides a compact and meaningful visual fingerprint of the text&rsquo;s meaning.</p>
<h2 id="the-deployment-pipeline-cicd-with-a-twist">The deployment pipeline: CI/CD with a twist</h2>
<p>A key challenge with this project was handling the model files.
Although I&rsquo;m using <em>quantized</em> versions of the weights, they are still quite heavy in megabytes, which is far too large to commit to a Git repository.
However, the application <em>needs</em> these files to be present in the <code>public/model</code> directory to function.</p>
<p>I solved this with a clever CI/CD pipeline using GitHub Actions.
Instead of storing the model in my repository, I download it on-the-fly during the deployment process.
Since the model files are local, when the application starts, they are faster to load than when loading them from 🤗 HuggingFace&rsquo;s hub.</p>
<p>Here’s the relevant snippet from my <code>.github/workflows/firebase-hosting-merge.yml</code> file:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">jobs</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">build_and_deploy</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">runs-on</span>:<span style="color:#bbb"> </span>ubuntu-latest<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">uses</span>:<span style="color:#bbb"> </span>actions/checkout@v4<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">name</span>:<span style="color:#bbb"> </span>Download Model<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">run</span>:<span style="color:#bbb"> </span>|<span style="color:#4070a0;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">          sudo apt-get install git-lfs
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">          git lfs install
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">          git clone https://huggingface.co/onnx-community/embeddinggemma-300m-ONNX public/model</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">run</span>:<span style="color:#bbb"> </span>npm ci &amp;&amp; npm run build<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">uses</span>:<span style="color:#bbb"> </span>FirebaseExtended/action-hosting-deploy@v0<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">with</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">repoToken</span>:<span style="color:#bbb"> </span>${{ secrets.GITHUB_TOKEN }}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">firebaseServiceAccount</span>:<span style="color:#bbb"> </span>${{ secrets.FIREBASE_SERVICE_ACCOUNT_EMBEDDING_GEMMA }}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#60a0b0;font-style:italic"># ... other options</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>This workflow does the following every time I push to the <code>main</code> branch:</p>
<ol>
<li>Checks out my code.</li>
<li><strong>Downloads the model:</strong> It installs <code>git-lfs</code> and clones the model files directly from the 🤗 HuggingFace Hub into the <code>public/model</code> directory. Note that it&rsquo;s certainly possible to cache the outcome of the model weight checkout though, to make the CI/CD pipeline snappier.</li>
<li><strong>Builds the app:</strong> It runs <code>npm ci &amp;&amp; npm run build</code>, which creates the static <code>dist</code> folder. Because the model is now in the <code>public</code> directory, Vite automatically includes it in the build output.</li>
<li><strong>Deploys:</strong> It sends the final <code>dist</code> folder, now containing the model, up to 🔥 Firebase Hosting.</li>
</ol>
<p>This approach gives me the best of both worlds: a lightweight Git repository and a fully functional, self-contained application deployed to users.</p>
<h2 id="conclusion">Conclusion</h2>
<p><strong>Google AI Studio</strong> and the <strong>Gemini CLI</strong> allowed me to easily build a demonstration app for running <strong>EmbeddingGemma</strong> on the client-side via <strong>Transformers.js</strong>.</p>
<p>This application requires <strong>no server</strong> (apart from the the static assets hosting), is <strong>privacy-focused</strong> (your data is never sent over the internet), and is <strong>cost-effective</strong> as its semantic search engine runs entirely in the browser.</p>
<p>I hope this project demonstrates the <strong>growing potential of client-side AI and small models</strong> (both embedding and language models).</p>
<p>I encourage you to explore the project and see it in action for yourself:</p>
<ul>
<li><strong>Live Demo:</strong> <a href="https://embedding-gemma.web.app/">https://embedding-gemma.web.app/</a>
<ul>
<li><em>Note: The first time you open the application, it will need to load the model files. You&rsquo;ll see a loading indicator, and once it&rsquo;s complete, you&rsquo;ll be able to start searching.</em></li>
</ul>
</li>
<li><strong>GitHub Repository:</strong> <a href="https://github.com/glaforge/embedding-gemma-semantic-search">https://github.com/glaforge/embedding-gemma-semantic-search</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Vibe-coding a Chrome extension with Gemini CLI to summarize articles</title><link>https://glaforge.dev/posts/2025/08/06/vibe-coding-a-chrome-extension-with-gemini-cli-to-summarize-articles/</link><pubDate>Wed, 06 Aug 2025 13:29:54 +0200</pubDate><guid>https://glaforge.dev/posts/2025/08/06/vibe-coding-a-chrome-extension-with-gemini-cli-to-summarize-articles/</guid><description>&lt;p>I often find myself staring at a wall of text online.
It could be a lengthy technical article, a detailed news report, or a deep-dive blog post.
My first thought is often: &lt;em>&amp;ldquo;Is this worth the time to read in full?&amp;rdquo;&lt;/em>
On top of that, for my podcast, &lt;a href="https://lescastcodeurs.com/">Les Cast Codeurs&lt;/a>, I&amp;rsquo;m constantly gathering links and need to create quick shownotes, which is essentially&amp;hellip; a summary.&lt;/p>
&lt;p>My first attempt to solve this was a custom &lt;a href="https://gemini.google/overview/gems/">Gemini Gems&lt;/a> I created: a personalized chatbot that could summarize links.
It worked, but I often ran into a wall: it couldn&amp;rsquo;t access paywalled content, pages that required a login, or dynamically generated sites that I was already viewing in my browser.
The solution was clear: I needed to bring the summarization &lt;em>to&lt;/em> the content, not the other way around.
The idea for a Chrome extension was born.&lt;/p></description><content:encoded>
<![CDATA[<p>I often find myself staring at a wall of text online.
It could be a lengthy technical article, a detailed news report, or a deep-dive blog post.
My first thought is often: <em>&ldquo;Is this worth the time to read in full?&rdquo;</em>
On top of that, for my podcast, <a href="https://lescastcodeurs.com/">Les Cast Codeurs</a>, I&rsquo;m constantly gathering links and need to create quick shownotes, which is essentially&hellip; a summary.</p>
<p>My first attempt to solve this was a custom <a href="https://gemini.google/overview/gems/">Gemini Gems</a> I created: a personalized chatbot that could summarize links.
It worked, but I often ran into a wall: it couldn&rsquo;t access paywalled content, pages that required a login, or dynamically generated sites that I was already viewing in my browser.
The solution was clear: I needed to bring the summarization <em>to</em> the content, not the other way around.
The idea for a Chrome extension was born.</p>
<p>This got me thinking: what if I had a browser extension that could give me the gist of any page with a single click?
I had the idea and the need, but one small problem: I&rsquo;d never built a Chrome extension before.</p>
<p>This project became an experiment in the trendy <em>&ldquo;vibe-coding&rdquo;</em> approach, and my partner in crime was the
<strong><a href="https://developers.google.com/gemini/cli?utm_campaign=CDR_0x7a40493f_default_b436838088&amp;utm_medium=external&amp;utm_source=blog">Gemini CLI</a></strong>.
I didn&rsquo;t start by reading pages of documentation.
Instead, I had a clear vision for what I wanted and decided to build it interactively.</p>
<p>What made the process so unique was that the Gemini CLI was incredibly proactive.
From the very beginning, when I just created an empty directory for the project, it immediately understood my intent.
Before I even had a chance to ask, it suggested that I probably wanted to create a Chrome extension for summarization and laid out a full plan, just by infering the intent because of the actual name of the directory I had created!</p>
<p>Throughout the development, it took liberties, suggesting better ways to handle the API key setup and proposing new features.
It felt less like giving instructions and more like a productive collaboration between a human and an AI.
The result is the <a href="https://github.com/glaforge/chrome-gemini-summarize-extension">Gemini summarizer Chrome extension</a>, and it was built entirely <em>on vibes</em>.</p>
<p>Of course, not everything is always roses and bloom, like when Gemini CLI reverted some of my manual changes (which lead me to warn it that I had actually made some manual changes, so that it could update its internal context), or when it somehow ran into a loop and not obeying my command, for some reason I couldn&rsquo;t really figure out.
Restarting it made it work: maybe like us, humans, it needs a break once in a while?
I&rsquo;m probably <em>anthropomorphising</em> too much, but overall, that was a productive experience!</p>
<h2 id="under-the-hood-powered-by-gemini">Under the hood: Powered by Gemini</h2>
<p>The core of this extension is, of course, the Google Gemini API. It&rsquo;s responsible for the powerful summarization capabilities.
To use the extension, you&rsquo;ll need to provide your own <a href="https://aistudio.google.com/app/apikey">Gemini API key</a>.
Don&rsquo;t worry, it&rsquo;s stored securely and locally in your browser&rsquo;s storage and is only used to communicate with the official Google AI endpoints.</p>
<h2 id="features-that-evolved-from-the-_vibe_">Features that evolved from the <em>vibe</em></h2>
<p>What started as a simple idea quickly grew as we built it. Here&rsquo;s what the extension can do:</p>
<ul>
<li><strong>Summarize web pages</strong>: Click the button to get a summary of the main article on a page.</li>
<li><strong>Summarize selected text only</strong>: If you highlight a specific piece of text, the extension is smart enough to summarize only your selection.</li>
<li><strong>Real-time streaming</strong>: The summary doesn&rsquo;t just appear; it streams in word-by-word, making the experience feel fast and responsive.</li>
<li><strong>Go shorter</strong>: Got a summary but want it even more concise? The <em>&ldquo;Shrink&rdquo;</em> button re-summarizes the summary to be as brief as possible.</li>
<li><strong>Speak my language</strong>: A simple toggle lets you get your summary in either English or French <em>(I&rsquo;m writing shownotes for the podcast in French).</em></li>
<li><strong>Copy with formatting</strong>: The copy button copies the summary as rich text, preserving all the formatting like bolding and bullet points. That way, it&rsquo;s easy to paste in a Google Docs, in a Slack channel or wherever you want.</li>
</ul>
<h2 id="potential-future-evolutions">Potential future evolutions</h2>
<p>This project is fully functional (at least for my personal need) but there are always more <em>vibes</em> to explore. Here are a few ideas for where this could go next:</p>
<ol>
<li><strong>Publish to the Chrome Web Store</strong>: To make installation as easy as a single click for any user.</li>
<li><strong>Expanded language support</strong>: Add a dropdown or input field to allow summarization in any language Gemini supports.</li>
<li><strong>Customizable prompts</strong>: Allow users to tweak the summarization prompts directly in the options page to tailor the style of the summaries to their exact needs. And maybe even support multiple prompts at the same time, that you can choose from another dropdown.</li>
</ol>
<h2 id="get-the-code-and-vibe-code-your-own-variant">Get the code and vibe code your own variant</h2>
<p>This entire project is open-source and available on GitHub. I encourage you to check it out, install it for yourself, and see how it feels.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Info</span>
      </div>
      <div class="admonition-content">
        <p>&#x27a1;&#xfe0f; <strong><a href="https://github.com/glaforge/chrome-gemini-summarize-extension">View the project on GitHub</a></strong></p>
      </div>
    </div><p>The <a href="https://github.com/glaforge/chrome-gemini-summarize-extension?tab=readme-ov-file#installation">installation</a>
is straightforward for developers, with instructions in the <code>README.md</code> file — until I decide it&rsquo;s worth sharing it more broadly via the Chrome extension store!</p>
<p>More importantly, I encourage you to take this as inspiration.
If you have an idea, even if you don&rsquo;t know the technology, try vibe-coding it.
I now find myself starting projects I wouldn&rsquo;t have dared to before because I wasn&rsquo;t familiar with the language, framework, or patterns.
I&rsquo;d think of a cool app idea but wouldn&rsquo;t even start, knowing it would take too many days or evenings just to get something basic going.
With vibe-coding, I can experiment and get a working prototype in a fraction of the time.</p>
<p>In the <em>pure</em> vibe-coding approach as <a href="https://x.com/karpathy/status/1886192184808149383">coined by Andrej Karpathy</a>,
you wouldn&rsquo;t even look at the generated code at all.
But I&rsquo;m a developer, I can&rsquo;t help it, and will always look at how my AI coding agent does its magic.</p>
<p>I didn&rsquo;t know how Chrome extensions were defined and worked, but now, I&rsquo;m more knowledgeable because I had the chance to witness how to build such an extension, and see the project structure, the key files needed (<code>manifest.json</code>), the use of service workers, how to store a secret like an API key in Chrome&rsquo;s storage, etc. It&rsquo;s a <strong>great learning opportunity where you actually look at the generated vibed-coded code</strong>!</p>
<p>That&rsquo;s the real power of this new way of building software: a great model like
<a href="https://cloud.google.com/vertex-ai/generative-ai/docs/models?utm_campaign=CDR_0x7a40493f_default_b436838088&amp;utm_medium=external&amp;utm_source=blog">Gemini</a> paired with a great tool like the
<a href="https://developers.google.com/gemini/cli?utm_campaign=CDR_0x7a40493f_default_b436838088&amp;utm_medium=external&amp;utm_source=blog">Gemini CLI</a>.
It&rsquo;s a game-changer.
Feel free to <a href="https://github.com/glaforge/chrome-gemini-summarize-extension">fork this project</a>, extend it, or build your own extension from scratch.
Happy vibe-coding!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Visualizing ADK multiagent systems</title><link>https://glaforge.dev/posts/2025/08/01/visualizing-adk-multiagent-systems/</link><pubDate>Fri, 01 Aug 2025 15:07:09 +0200</pubDate><guid>https://glaforge.dev/posts/2025/08/01/visualizing-adk-multiagent-systems/</guid><description>&lt;p>Let me share an interesting experiment I worked on to visualize your AI agent structure, more specifically, &lt;a href="https://github.com/google/adk-java">Agent Development Kit&lt;/a> (ADK) multiagents.&lt;/p>
&lt;p>The more complex your agents become, as you split tasks and spin off more specialized and focused sub-agents, the harder it is to see what your system is really made of, and how the interactions happen between the various components.&lt;/p>
&lt;p>This is also something I experienced when I was &lt;a href="https://glaforge.dev/tags/workflows/">covering&lt;/a> &lt;a href="https://cloud.google.com/workflows">Google Cloud Workflows&lt;/a>: the more steps in the workflow, the more loops I had, indirections, conditions, etc, the trickier it was to understand and debug.
And sometimes, as the saying goes, a picture is worth a thousand words.
So when I was working on my recent &lt;a href="https://glaforge.dev/posts/2025/07/29/mastering-agentic-workflows-with-adk-the-recap/">series of articles on ADK agentic workflows&lt;/a>
(drawing diagrams by hand) this idea of experimenting with an ADK agent &lt;em>visualizer&lt;/em> came up immediately.&lt;/p></description><content:encoded>
<![CDATA[<p>Let me share an interesting experiment I worked on to visualize your AI agent structure, more specifically, <a href="https://github.com/google/adk-java">Agent Development Kit</a> (ADK) multiagents.</p>
<p>The more complex your agents become, as you split tasks and spin off more specialized and focused sub-agents, the harder it is to see what your system is really made of, and how the interactions happen between the various components.</p>
<p>This is also something I experienced when I was <a href="https://glaforge.dev/tags/workflows/">covering</a> <a href="https://cloud.google.com/workflows">Google Cloud Workflows</a>: the more steps in the workflow, the more loops I had, indirections, conditions, etc, the trickier it was to understand and debug.
And sometimes, as the saying goes, a picture is worth a thousand words.
So when I was working on my recent <a href="https://glaforge.dev/posts/2025/07/29/mastering-agentic-workflows-with-adk-the-recap/">series of articles on ADK agentic workflows</a>
(drawing diagrams by hand) this idea of experimenting with an ADK agent <em>visualizer</em> came up immediately.</p>
<p>Let me introduce you to my <strong>ADK Agent Code Visualizer</strong>, a tool designed to give you a clear, visual representation of your agent-based applications built with the <a href="https://developers.google.com/ai/agent-development-kit?utm_campaign=CDR_0x7a40493f_default_b435663830&amp;utm_medium=external&amp;utm_source=blog">Agent Development Kit (ADK)</a>.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition warning">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>
        <span>Disclaimer</span>
      </div>
      <div class="admonition-content">
        <p>This is an experiment, not really something I intend to develop that much further.
It works fine for single-file systems that you can easily copy&rsquo;n paste in the visualizer.
But it won&rsquo;t understand your current project structure, and your agents spanning multipe files.</p>
<p>&#x27a1;&#xfe0f; That said, for those who want to play with it already without further ado, please head over to this online version of my
<a href="https://adk-agent-code-visualizer-1029513523185.europe-west1.run.app/">ADK code visualizer</a>.</p>
<p>&#x1f4c2; I&rsquo;ve also pushed the code of this <a href="https://github.com/glaforge/adk-agent-code-visualizer">project to GitHub</a>
if you want to take a look or further extend it.</p>
      </div>
    </div><h2 id="from-vibe-coding-to-cloud-deployment">From vibe-coding to cloud deployment</h2>
<p>The journey of this project is a testament to the power of modern AI-assisted development.
It all started in <a href="https://aistudio.google.com/">Google AI Studio</a>, where I <em>&ldquo;vibe-coded&rdquo;</em> the initial concept.
I provided a high-level description of what I wanted to build, and AI Studio generated the foundational React and Node.js code.
From there, I moved to my local environment and used <a href="https://cloud.google.com/gemini/docs/codeassist/gemini-cli?utm_campaign=CDR_0x7a40493f_default_b435663830&amp;utm_medium=external&amp;utm_source=blog">Gemini CLI</a>
for the iterative development process—adding features, refining the logic, and fixing bugs.
The entire development experience was a seamless collaboration between human and AI. Yeah!</p>
<p>Finally, the application was deployed on <strong>Google Cloud</strong>, more specifically containerized on <a href="https://cloud.google.com/run?utm_campaign=CDR_0x7a40493f_default_b435663830&amp;utm_medium=external&amp;utm_source=blog">Cloud Run</a>
making it accessible for everyone to use.</p>
<h2 id="what-it-does">What it does</h2>
<p>The ADK Agent Code Visualizer takes a single source file (Python or Java) containing an ADK agent definition and generates an interactive graph
(using <a href="https://reactflow.dev/">ReactFlow</a>).
This visualization helps you understand the architecture of your multi-agent system at a glance.</p>
<p>Here’s what the visualization highlights:</p>
<ul>
<li><strong>Agent relationships:</strong> See how your agents are connected. <a href="https://glaforge.dev/posts/2025/07/23/mastering-agentic-workflows-with-adk-sub-agents/">Sub-agents</a> are clearly marked with arrows indicating the flow of control.</li>
<li><strong>Tool usage:</strong> If an agent uses one or more <a href="https://glaforge.dev/posts/2025/06/15/expanding-ai-agent-capabilities-with-tools/">tools</a>, it&rsquo;s indicated on the graph, giving you insight into the agent&rsquo;s capabilities.</li>
<li><strong>Callbacks:</strong> The visualization also flags when callbacks are defined for an agent.</li>
<li><strong>Orchestration patterns:</strong> The graph shows whether agents are configured to run:
<ul>
<li><strong>Sequentially:</strong> <a href="https://glaforge.dev/posts/2025/07/24/mastering-agentic-workflows-with-adk-sequential-agent/">One after another</a>.</li>
<li><strong>In parallel:</strong> Executing <a href="https://glaforge.dev/posts/2025/07/25/mastering-agentic-workflows-with-adk-parallel-agent/">concurrently</a>.</li>
<li><strong>In a loop:</strong> <a href="https://glaforge.dev/posts/2025/07/28/mastering-agentic-workflows-with-adk-loop-agents/">Repeating a series of actions</a>.</li>
</ul>
</li>
</ul>
<p>The visualization is composed of four main types of nodes (with different color codes), each representing a different kind of agent:</p>
<ul>
<li><code>LlmAgent</code>: A plain agent node.</li>
<li><code>SequentialAgent</code>: For agents that execute in a sequence.</li>
<li><code>ParallelAgent</code>: For agents that run concurrently.</li>
<li><code>LoopAgent</code>: For agents that repeat actions in a loop.</li>
</ul>
<p>Whenever there are sub-agents or linked agents, you&rsquo;ll see arrows pointing from the parent or preceding agent to the next one in the flow, making the orchestration logic easy to follow.</p>
<h3 id="dark-mode">Dark mode!</h3>
<p>And for all the nerds who prefer dark mode, you can easily switch between light and dark modes!</p>
<p><figure>
  <a href="#img-77d157d928073605b17fe814341d7356">
    <img src="/img/adk/visualizer-dark-light-mode.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-77d157d928073605b17fe814341d7356">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/adk/visualizer-dark-light-mode.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h2 id="limitations">Limitations</h2>
<p>As I said in introduction, it&rsquo;s important to note a key limitation of the current version: <strong>the tool analyzes single source files only.</strong>
This is more of a proof-of-concept than a finalized product.
This means it does not parse an entire multi-file project to resolve dependencies or definitions across different files
(externalizing prompts, splitting in different files or directories, etc).</p>
<p>Despite this, the visualizer works pretty well for the kind of self-contained examples you&rsquo;ll find in the official
<a href="https://google.github.io/adk-docs/">ADK documentation</a>
and the associated <a href="https://github.com/google/adk-samples">adk-samples GitHub repository</a>.
It&rsquo;s a perfect companion for learning ADK or for quickly mapping out the structure of your single-file agent orchestrations.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The ADK Agent Code Visualizer is a simple yet powerful tool for anyone working with the Agent Development Kit.
It streamlines the process of understanding complex agent interactions and serves as a great example of how modern AI tools like
<a href="https://aistudio.google.com/">Google AI Studio</a>, and
<a href="https://cloud.google.com/gemini/docs/codeassist/gemini-cli?utm_campaign=CDR_0x7a40493f_default_b435663830&amp;utm_medium=external&amp;utm_source=blog">Gemini CLI</a> can accelerate the development lifecycle from idea to deployment.</p>
<p>Feel free to check it out and visualize your own agent creations!
And tell me what you think!</p>
<p>&#x27a1;&#xfe0f; You can <a href="https://adk-agent-code-visualizer-1029513523185.europe-west1.run.app/">try it online</a>
and check out the <a href="https://github.com/glaforge/adk-agent-code-visualizer">source code</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Mastering agentic workflows with ADK: the recap</title><link>https://glaforge.dev/posts/2025/07/29/mastering-agentic-workflows-with-adk-the-recap/</link><pubDate>Tue, 29 Jul 2025 08:51:28 +0200</pubDate><guid>https://glaforge.dev/posts/2025/07/29/mastering-agentic-workflows-with-adk-the-recap/</guid><description>&lt;p>Over the past few articles, we&amp;rsquo;ve taken a deep dive into the powerful &lt;strong>agentic workflow orchestration&lt;/strong> capabilities of the &lt;a href="https://github.com/google/adk-java">Agent Development Kit&lt;/a> (ADK) for &lt;strong>Java&lt;/strong>. We&amp;rsquo;ve seen how to build robust, specialized AI agents by moving beyond single, monolithic agents. We&amp;rsquo;ve explored how to structure our agents for:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Part 1&lt;/strong>: &lt;a href="https://glaforge.dev/posts/2025/07/23/mastering-agentic-workflows-with-adk-sub-agents/">Flexibility with sub-agents&lt;/a> — Letting an orchestrator LLM decide the best course of action.&lt;/li>
&lt;li>&lt;strong>Part 2&lt;/strong>: &lt;a href="https://glaforge.dev/posts/2025/07/24/mastering-agentic-workflows-with-adk-sequential-agent/">Order with sequential agents&lt;/a> — Enforcing a strict, predictable execution path.&lt;/li>
&lt;li>&lt;strong>Part 3&lt;/strong>: &lt;a href="https://glaforge.dev/posts/2025/07/25/mastering-agentic-workflows-with-adk-parallel-agent/">Efficiency with parallel agents&lt;/a> — Running independent tasks concurrently to save time.&lt;/li>
&lt;li>&lt;strong>Part 4&lt;/strong>: &lt;a href="https://glaforge.dev/posts/2025/07/28/mastering-agentic-workflows-with-adk-loop-agents/">Refinement with loop agents&lt;/a> — Creating iterative processes for self-correction and complex problem-solving.&lt;/li>
&lt;/ul>
&lt;p>In this final post, let&amp;rsquo;s bring it all together. We&amp;rsquo;ll summarize each pattern, clarify when to use one over the other, and show how their true power is unlocked when you start combining them.&lt;/p></description><content:encoded>
<![CDATA[<p>Over the past few articles, we&rsquo;ve taken a deep dive into the powerful <strong>agentic workflow orchestration</strong> capabilities of the <a href="https://github.com/google/adk-java">Agent Development Kit</a> (ADK) for <strong>Java</strong>. We&rsquo;ve seen how to build robust, specialized AI agents by moving beyond single, monolithic agents. We&rsquo;ve explored how to structure our agents for:</p>
<ul>
<li><strong>Part 1</strong>: <a href="https://glaforge.dev/posts/2025/07/23/mastering-agentic-workflows-with-adk-sub-agents/">Flexibility with sub-agents</a> — Letting an orchestrator LLM decide the best course of action.</li>
<li><strong>Part 2</strong>: <a href="https://glaforge.dev/posts/2025/07/24/mastering-agentic-workflows-with-adk-sequential-agent/">Order with sequential agents</a> — Enforcing a strict, predictable execution path.</li>
<li><strong>Part 3</strong>: <a href="https://glaforge.dev/posts/2025/07/25/mastering-agentic-workflows-with-adk-parallel-agent/">Efficiency with parallel agents</a> — Running independent tasks concurrently to save time.</li>
<li><strong>Part 4</strong>: <a href="https://glaforge.dev/posts/2025/07/28/mastering-agentic-workflows-with-adk-loop-agents/">Refinement with loop agents</a> — Creating iterative processes for self-correction and complex problem-solving.</li>
</ul>
<p>In this final post, let&rsquo;s bring it all together. We&rsquo;ll summarize each pattern, clarify when to use one over the other, and show how their true power is unlocked when you start combining them.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition tip">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M272 384c9.6-31.9 29.5-59.1 49.2-86.2c0 0 0 0 0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4c0 0 0 0 0 0c19.8 27.1 39.7 54.4 49.2 86.2l160 0zM192 512c44.2 0 80-35.8 80-80l0-16-160 0 0 16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z"/></svg>
        <span>For the impatient</span>
      </div>
      <div class="admonition-content">
        <p>You can also check out this video, generated by my colleague <a href="https://medium.com/@iromin">Romin Irani</a> thanks to the new <strong>Video Overview</strong> feature of <a href="https://notebooklm.google.com/">NotebookLM</a>:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/HqNSvstMlNs?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>Then dive into the articles to get the full details!</p>
      </div>
    </div><h2 id="a-quick-recap-your-agentic-workflow-cheat-sheet">A quick recap: your agentic workflow cheat sheet</h2>
<p>Choosing the right workflow is about matching the structure of your problem to the structure of your agent system. Here’s a quick guide:</p>
<table>
  <thead>
      <tr>
          <th>Workflow</th>
          <th>Key ADK Class</th>
          <th>Use Case</th>
          <th>Pros</th>
          <th>Cons</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Sub-Agents</strong></td>
          <td><code>LlmAgent</code></td>
          <td>User-driven, flexible tasks where the next step is not always known.</td>
          <td>High flexibility, conversational, great for user-facing bots.</td>
          <td>Less predictable, relies on LLM reasoning for flow control.</td>
      </tr>
      <tr>
          <td><strong>Sequential</strong></td>
          <td><code>SequentialAgent</code></td>
          <td>Fixed, multi-step processes where order is critical.</td>
          <td>Predictable, reliable, easy to debug, guarantees order.</td>
          <td>Inflexible, can be slower if tasks could be parallelized.</td>
      </tr>
      <tr>
          <td><strong>Parallel</strong></td>
          <td><code>ParallelAgent</code></td>
          <td>Gathering data from multiple sources or running independent tasks.</td>
          <td>Highly efficient, significantly reduces latency for I/O-bound tasks.</td>
          <td>All tasks run; no short-circuiting. Less suitable for tasks with dependencies.</td>
      </tr>
      <tr>
          <td><strong>Loop</strong></td>
          <td><code>LoopAgent</code></td>
          <td>Iterative refinement, self-correction, or processes that repeat until a condition is met.</td>
          <td>Powerful for complex problem-solving, enables agents to improve their own work.</td>
          <td>Can lead to infinite loops if not designed carefully (always use <code>maxIterations</code>!).</td>
      </tr>
  </tbody>
</table>
<h2 id="when-to-use-each-workflow">When to use each workflow</h2>
<p>Let&rsquo;s distill the decision-making process down to its essence.</p>
<h3 id="choose-sub-agents-for-flexibility-and-conversation">Choose sub-agents for flexibility and conversation</h3>
<p>The <code>LlmAgent</code> with a team of sub-agents is your go-to for building conversational assistants. This <em>&ldquo;divide and conquer&rdquo;</em> strategy gives an orchestrator LLM the autonomy and agency to choose the right specialist (sub-agent) for the job based on the user&rsquo;s request.</p>
<h4 id="use-it-when">Use it when:</h4>
<ul>
<li>The user is in control, and the conversation can go in many different directions.</li>
<li>You need to delegate to a wide range of tools or specialists.</li>
<li>The exact sequence of operations is not — and should not be — predetermined.</li>
</ul>
<h3 id="choose-sequentialagent-for-order-and-predictability">Choose <code>SequentialAgent</code> for order and predictability</h3>
<p>When you have a process that must follow a specific order, the <code>SequentialAgent</code> is the perfect tool. It creates a fixed pipeline where the output of one step becomes the input for the next. This provides structure and guarantees a consistent outcome.</p>
<h4 id="use-it-when-1">Use it when:</h4>
<ul>
<li>You are automating a business process, like <em>&ldquo;Step A, then Step B, then Step C.&rdquo;</em></li>
<li>The outcome of one agent is a necessary prerequisite for the next.</li>
<li>You need a deterministic and easily debuggable workflow.</li>
</ul>
<h3 id="choose-parallelagent-for-efficiency-and-speed">Choose <code>ParallelAgent</code> for efficiency and speed</h3>
<p>If your workflow involves multiple independent tasks—like fetching data from different APIs or performing separate
analyses—running them sequentially is a waste of time. The <code>ParallelAgent</code> executes these tasks concurrently, dramatically speeding up the total execution time.</p>
<h4 id="use-it-when-2">Use it when:</h4>
<ul>
<li>You have multiple I/O-bound tasks (e.g., web searches, database queries).</li>
<li>The tasks do not depend on each other&rsquo;s results.</li>
<li>Minimizing latency is a primary concern.</li>
</ul>
<h3 id="choose-loopagent-for-iteration-and-refinement">Choose <code>LoopAgent</code> for iteration and refinement</h3>
<p>Some problems can&rsquo;t be solved in a single shot. For tasks that require trial, feedback, and correction — like generating code and then having a reviewer agent critique it — the <code>LoopAgent</code> is indispensable.
It automates the cycle of <em>&ldquo;do, check, refine.&rdquo;</em></p>
<h4 id="use-it-when-3">Use it when:</h4>
<p>An agent needs to improve its work based on feedback (from another agent or a tool).
You are building a system that needs to work towards a goal state through iteration.
The task involves complex problem-solving that benefits from a trial-and-error approach.</p>
<h2 id="the-power-of-composition-better-together">The Power of composition: better together</h2>
<p>While each workflow is powerful on its own, <strong>the real magic happens when you start composing them</strong>.
This is the core philosophy of the ADK: building sophisticated systems from simple, modular blocks.</p>
<p>In our series, we saw a prime example of this with the <code>company-detective</code> agent. It was a <code>SequentialAgent</code> that orchestrated a two-step process:</p>
<ul>
<li>A <code>ParallelAgent</code> that ran three different research agents concurrently (<code>company-profiler</code>, <code>news-finder</code>, <code>financial-analyst</code>).</li>
<li>A final <code>LlmAgent</code> (<code>report-compiler</code>) that took the aggregated results from the parallel step and synthesized them into a final report.</li>
</ul>
<p>This hybrid approach gave us the best of both worlds:
the raw speed of parallel execution for data gathering and the structured order of a sequential pipeline to ensure the final report was only compiled after all the research was complete.</p>
<p>Similarly, we built a code-refiner-assistant by embedding a <code>LoopAgent</code> inside a <code>SequentialAgent</code>.
This allowed us to first iteratively generate and review code until it was perfect, and then proceed to the final step of presenting it to the user.</p>
<h2 id="conclusion-determinism-and-control-for-better-ai">Conclusion: determinism and control for better AI</h2>
<p>By breaking down complex problems and assigning them to specialized agentic workflows, we gain more control and produce more reliable outcomes. Instead of relying on a single, massive LLM to figure everything out, we guide the process.
We trade a little bit of the LLM&rsquo;s raw agency for a massive gain in predictability, maintainability, and overall quality.</p>
<p>The <a href="https://github.com/google/adk-java">ADK for Java</a> gives you the toolkit to be a true AI architect.
By mastering and combining these fundamental patterns (sub-agents, sequential, parallel, and loop) you can move beyond simple bots and start building genuinely capable and robust AI systems that solve real-world problems in a structured and deterministic way.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Mastering agentic workflows with ADK: Loop agents</title><link>https://glaforge.dev/posts/2025/07/28/mastering-agentic-workflows-with-adk-loop-agents/</link><pubDate>Mon, 28 Jul 2025 09:37:02 +0200</pubDate><guid>https://glaforge.dev/posts/2025/07/28/mastering-agentic-workflows-with-adk-loop-agents/</guid><description>&lt;p>Welcome to the final installment of our series on &lt;strong>mastering agentic workflows&lt;/strong> with the &lt;a href="https://github.com/google/adk-java">ADK for Java&lt;/a>.
We&amp;rsquo;ve covered a lot of ground:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://glaforge.dev/posts/2025/07/23/mastering-agentic-workflows-with-adk-sub-agents/">Part 1: &lt;strong>Sub-agents&lt;/strong>&lt;/a> for flexible, user-driven delegation.&lt;/li>
&lt;li>&lt;a href="https://glaforge.dev/posts/2025/07/24/mastering-agentic-workflows-with-adk-sequential-agent/">Part 2: &lt;strong>Sequential agents&lt;/strong>&lt;/a> for predictable, ordered processes.&lt;/li>
&lt;li>&lt;a href="https://glaforge.dev/posts/2025/07/25/mastering-agentic-workflows-with-adk-parallel-agent/">Part 3: &lt;strong>Parallel agents&lt;/strong>&lt;/a> for efficient, concurrent execution.&lt;/li>
&lt;/ul>
&lt;p>Now, we&amp;rsquo;ll explore a pattern that enables agents to mimic a fundamental human problem-solving technique: &lt;strong>iteration&lt;/strong>.
For tasks that require refinement, trial-and-error, and self-correction, the ADK provides a &lt;strong>&lt;code>LoopAgent&lt;/code>&lt;/strong>.&lt;/p></description><content:encoded>
<![CDATA[<p>Welcome to the final installment of our series on <strong>mastering agentic workflows</strong> with the <a href="https://github.com/google/adk-java">ADK for Java</a>.
We&rsquo;ve covered a lot of ground:</p>
<ul>
<li><a href="https://glaforge.dev/posts/2025/07/23/mastering-agentic-workflows-with-adk-sub-agents/">Part 1: <strong>Sub-agents</strong></a> for flexible, user-driven delegation.</li>
<li><a href="https://glaforge.dev/posts/2025/07/24/mastering-agentic-workflows-with-adk-sequential-agent/">Part 2: <strong>Sequential agents</strong></a> for predictable, ordered processes.</li>
<li><a href="https://glaforge.dev/posts/2025/07/25/mastering-agentic-workflows-with-adk-parallel-agent/">Part 3: <strong>Parallel agents</strong></a> for efficient, concurrent execution.</li>
</ul>
<p>Now, we&rsquo;ll explore a pattern that enables agents to mimic a fundamental human problem-solving technique: <strong>iteration</strong>.
For tasks that require refinement, trial-and-error, and self-correction, the ADK provides a <strong><code>LoopAgent</code></strong>.</p>
<h2 id="building-agents-that-refine-their-work">Building agents that refine their work</h2>
<p>Think about how you write code. You write a first draft, you review it (or a colleague does), you find issues, and you refine it.
You repeat this cycle until the code is correct. The <code>LoopAgent</code> is designed to automate exactly this kind of iterative process.</p>
<p>A loop workflow is perfect for:</p>
<ul>
<li><strong>Complex problem-solving:</strong> Where the agent needs to try an approach, evaluate the outcome, and then try again.</li>
<li><strong>Self-correction:</strong> Building systems that can review and improve their own output.</li>
<li><strong>Reaching a goal state:</strong> Continuing a process until a specific condition is met.</li>
</ul>
<h2 id="a-practical-example-a-code-refiner-assistant">A practical example: a <code>code-refiner-assistant</code></h2>
<p>Let&rsquo;s look at our example of the day.
I&rsquo;ve built a <code>code-refiner-assistant</code> that iteratively generates and reviews a Python function until it meets the required standards.</p>
<p>Let&rsquo;s detail its components, but first, a visual diagram will help understand the flow:</p>
<p><figure>
  <a href="#img-aaa4f7d983f6538eb20020ba41084eaa">
    <img src="/img/adk/code-refiner-assistant.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-aaa4f7d983f6538eb20020ba41084eaa">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/adk/code-refiner-assistant.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>First, we&rsquo;ll need a <code>code-generator</code> agent:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>codeGenerator<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;code-generator&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Writes and refines code based on a request and feedback.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to write a Python function
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        based on the user&#39;s request.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        On the first turn, write the initial version of the code.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        On subsequent turns, you will receive feedback on your code.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your task is to refine the code based on this feedback.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Previous feedback (if any):
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {feedback?}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">outputKey</span>(<span style="color:#4070a0">&#34;generated_code&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Notice two things:</p>
<ul>
<li>The result of this agent will be stored in the <code>generated_code</code> output key (in the agent&rsquo;s state).</li>
<li>As this agent will be part of the refinement loop, it will look at the <code>feedback</code> that it might receive from a previous iteration of the loop. The <code>?</code> character signals the <code>feedback</code> state variable may be absent, which is the case for the first iteration. The question mark indicates that no exception should be thrown if this variable isn&rsquo;t present (again, in the case of the first iteration) this placeholder will just be replaced by an empty string.</li>
</ul>
<p>Next, we need a <code>code-reviewer</code> agent to judge whether the code is complete or needs work:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>codeReviewer<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;code-reviewer&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Reviews code and decides if it&#39;s complete or needs work.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to act as a senior code reviewer.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Analyze the provided Python code for correctness,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        style, and potential bugs.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Code to review:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {generated_code}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">outputKey</span>(<span style="color:#4070a0">&#34;feedback&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>It takes in input the <code>generated_code</code> output from the <code>code-generator</code> agent.
And it will store its result in the <code>feedback</code> state variable, so that the <code>code-generator</code> can update the code if needed.</p>
<p>Now, let&rsquo;s put the two agents in a loop:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>codeRefinerLoop<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LoopAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;code-refiner-loop&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Iteratively generates and reviews code until it is correct.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">subAgents</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>codeGenerator,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>codeReviewer)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">maxIterations</span>(5)<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// Safety net!</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>This coder/refiner loop is the first step of a sequential agent,
and the last step is the following <code>final-presenter</code> agent that displays the resulting reviewed code:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>finalPresenter<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;final-presenter&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Presents the final, accepted code to the user.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        The code has been successfully generated and reviewed.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Present the final version to the user in a clear format.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Final Code:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {generated_code}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Let&rsquo;s finalize this setup with the code of the sequential agent, combining the <code>code-refiner-loop</code> and <code>final-presenter</code> agents:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>SequentialAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;code-refiner-assistant&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Manages the full code generation and refinement process.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">subAgents</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>codeRefinerLoop,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>finalPresenter)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="the-safety-net-maxiterations">The safety net: <code>maxIterations</code></h2>
<p>If we were running this agent as-is, it could run endlessly, looping over the code generator and code reviewer.
Fortunately, we added the instruction <code>maxIterations(5)</code> to the <code>LoopAgent</code> definition to avoid this situation.
No more than 5 iterations are allowed here.</p>
<p>You should <strong>always specify a maximum number of iterations</strong>, otherwise your agents could be stuck in an endless loop.</p>
<p>Now, we would like to stop the iteration once a certain condition is met:
when we&rsquo;re happy with the quality of the generated code, when the code doesn&rsquo;t need to be further improved.</p>
<h2 id="exiting-the-loop-early-setescalatetrue">Exiting the loop early: <code>setEscalate(true)</code></h2>
<p>An uncontrolled loop is a dangerous thing. To stop a <code>LoopAgent</code>, there is one fundamental mechanism: calling <code>setEscalate(true)</code> on the context&rsquo;s event actions.
This is the universal <em>&ldquo;break&rdquo;</em> statement that tells the ADK runtime to stop the current agent and pass control to its parent.</p>
<p>The key to building robust loops is understanding the two main strategies for invoking this critical method.</p>
<h3 id="strategy-1-in-flight-escalation-with-a-functiontool">Strategy #1: in-flight escalation with a <code>FunctionTool</code></h3>
<p>This approach treats exiting the loop as a primary, explicit action the agent must perform.</p>
<p>First, we define a simple Java method in our class to serve as the tool&rsquo;s implementation.
This method does one powerful thing: it calls <code>setEscalate(true)</code>.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Schema</span>(description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Call this function ONLY when the code-reviewer
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    agent indicates no further changes are needed,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    signaling the iterative process should end.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>Map<span style="color:#666">&lt;</span>String,<span style="color:#bbb"> </span>Object<span style="color:#666">&gt;</span><span style="color:#bbb"> </span><span style="color:#06287e">exitLoop</span>(ToolContext<span style="color:#bbb"> </span>toolContext)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>toolContext.<span style="color:#4070a0">eventActions</span>().<span style="color:#4070a0">setEscalate</span>(<span style="color:#007020;font-weight:bold">true</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Map.<span style="color:#4070a0">of</span>();<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// Return value can an empty map</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Next, we create an instance of <code>FunctionTool</code> and register it with our <code>code-reviewer</code> agent.
We also instruct the agent that it <strong>MUST</strong> call this tool when it&rsquo;s satisfied.</p>
<p>Let&rsquo;s have a look at the modified <code>code-reviewer</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>codeReviewer<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;code-reviewer&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Reviews code and decides if it&#39;s complete
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        or needs more work.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to act as a senior code reviewer.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Analyze the provided Python code for correctness,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        style, and potential bugs.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Code to review:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {generated_code}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        If the code is perfect and meets the user&#39;s request,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        you MUST call the `exitLoop` tool.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Otherwise, provide constructive feedback
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        for the `code-generator` to improve the code.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">outputKey</span>(<span style="color:#4070a0">&#34;feedback&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(FunctionTool.<span style="color:#4070a0">create</span>(<span style="color:#007020;font-weight:bold">this</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;exitLoop&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>When the agent calls this tool, the escalation happens <em>during</em> the agent&rsquo;s turn, interrupting the normal flow.
This pattern is powerful because the decision to exit is a deliberate, observable action in the agent&rsquo;s execution trace.</p>
<h3 id="strategy-2-programmatic-escalation-via-callbacks">Strategy #2: programmatic escalation via <code>Callback</code>s</h3>
<p>This approach is more programmatic.
Instead of a tool, we instruct the <code>code-reviewer</code> to output a simple keyword (like &ldquo;EXIT&rdquo;) when it&rsquo;s done.
Then, we use a callback on the following agent to check for this keyword.</p>
<p>However, there&rsquo;s an important subtlety: a callback runs <em>before</em> or <em>after</em> an agent&rsquo;s turn starts or is complete.
Simply calling <code>setEscalate(true)</code> inside a callback is not enough, because the runtime might not <em>&ldquo;notice&rdquo;</em> it.</p>
<p><strong>To ensure the escalation is processed</strong>, the callback must also do one of two things:</p>
<ol>
<li><strong>Modify the state</strong> (so the state delta is not empty).</li>
<li><strong>Return a non-empty <code>Maybe</code></strong> (e.g., <code>Maybe.just(...)</code>).</li>
</ol>
<p>This signals to the runtime that something has changed, prompting it to check for the escalation flag.
Returning <code>Maybe.empty()</code> in a callback is equivalent to actually doing nothing!</p>
<p>For callbacks, there are two approaches: we can draw a parallel to classic programming loops: <code>do/while</code> and <code>while</code>,
depending on whether you want to check before or after an agent runs to exit the loop.</p>
<p><figure>
  <a href="#img-b8a6c307949d194e1a4fb5cfbfea5441">
    <img src="/img/adk/adk-before-after-agent-callback.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-b8a6c307949d194e1a4fb5cfbfea5441">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/adk/adk-before-after-agent-callback.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h4 id="the-dowhile-approach-afteragentcallback">The <code>do/while</code> approach: <code>afterAgentCallback</code></h4>
<p>This pattern checks the exit condition <em>after</em> the main work of the iteration is done.
We place an <code>afterAgentCallback</code> usually on the <em>last</em> agent in the loop (<code>code-reviewer</code>).
But you could also decide to exit the loop mid-way as well, depending on your use case.</p>
<p>It&rsquo;s important to also tweak the instructions to force the agent to return a particular magic word,
so that the callback can check the result and decide whether to exit.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>codeReviewer<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;code-reviewer&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Reviews code and decides if it&#39;s complete or needs more work.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to act as a senior code reviewer.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Analyze the provided Python code for correctness,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        style, and potential bugs.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Code to review:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {generated_code}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        If the code is perfect and meets the user&#39;s request,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        you MUST reply with just one single word: EXIT
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Don&#39;t add any introduction or commentary.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Just reply with EXIT.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Otherwise, provide constructive feedback
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        for the `code-generator to improve the code.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">outputKey</span>(<span style="color:#4070a0">&#34;feedback&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">afterAgentCallback</span>(callbackContext<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>feedback<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>callbackContext.<span style="color:#4070a0">state</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">getOrDefault</span>(<span style="color:#4070a0">&#34;feedback&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#34;</span>).<span style="color:#4070a0">toString</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(feedback.<span style="color:#4070a0">trim</span>().<span style="color:#4070a0">equalsIgnoreCase</span>(<span style="color:#4070a0">&#34;EXIT&#34;</span>))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>callbackContext.<span style="color:#4070a0">eventActions</span>().<span style="color:#4070a0">setEscalate</span>(<span style="color:#007020;font-weight:bold">true</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>callbackContext.<span style="color:#4070a0">state</span>().<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;review&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;OK&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Maybe.<span style="color:#4070a0">just</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>Content.<span style="color:#4070a0">fromParts</span>(Part.<span style="color:#4070a0">fromText</span>(<span style="color:#4070a0">&#34;EXIT&#34;</span>)));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Maybe.<span style="color:#4070a0">empty</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>})<span style="color:#bbb">
</span></span></span></code></pre></div><p>The loop body (both <code>code-generator</code> and <code>code-reviewer</code>) always runs.
Then, the callback inspects the result.
Because the state is changed with a new <code>review</code> state variable,
and because we return a non-empty <code>Maybe</code>,
the runtime will process the <code>setEscalate(true)</code> flag and stop the next iteration.</p>
<p>Note that only one of setting a new state variable, and returning a non-empty <code>Maybe</code> is enough:
no need to do both like in this example.</p>
<h4 id="the-while-approach-beforeagentcallback">The <code>while</code> approach: <code>beforeAgentCallback</code></h4>
<p>This pattern checks the exit condition <em>before</em> the iteration begins.
We place a <code>beforeAgentCallback</code> on the <em>first</em> agent in the loop (<code>code-generator</code>).
So the idea is to check whether a condition is met before starting a new iteration.</p>
<p>In our example, that applies to the <code>code-generator</code>,
but there&rsquo;s no need to change the prompt this time,
only adding the <code>beforeAgentCallback</code> is needed:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>codeGenerator<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;code-generator&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Writes and refines code based on a request and feedback.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to write a Python function
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        based on the user&#39;s request.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        On the first turn, write the initial version of the code.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        On subsequent turns, you will receive feedback on your code.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your task is to refine the code based on this feedback.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Previous feedback (if any):
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {feedback?}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">outputKey</span>(<span style="color:#4070a0">&#34;generated_code&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">beforeAgentCallback</span>(callbackContext<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>feedback<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>callbackContext.<span style="color:#4070a0">state</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">getOrDefault</span>(<span style="color:#4070a0">&#34;feedback&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#34;</span>).<span style="color:#4070a0">toString</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(feedback.<span style="color:#4070a0">trim</span>().<span style="color:#4070a0">equalsIgnoreCase</span>(<span style="color:#4070a0">&#34;EXIT&#34;</span>))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>callbackContext.<span style="color:#4070a0">eventActions</span>().<span style="color:#4070a0">setEscalate</span>(<span style="color:#007020;font-weight:bold">true</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Maybe.<span style="color:#4070a0">just</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>Content.<span style="color:#4070a0">fromParts</span>(Part.<span style="color:#4070a0">fromText</span>(<span style="color:#4070a0">&#34;EXIT&#34;</span>)));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Maybe.<span style="color:#4070a0">empty</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>})<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>This is highly efficient. It checks the result from the last turn, and if it&rsquo;s time to exit,
it escalates <em>before</em> running the <code>code-generator</code>, saving an unnecessary LLM call.</p>
<h3 id="which-strategy-is-better">Which strategy is better?</h3>
<p>There&rsquo;s no single answer.</p>
<ul>
<li>The <strong><code>FunctionTool</code></strong> approach is excellent when the decision to exit is a core part of the agent&rsquo;s responsibility and should be an explicit action.</li>
<li>The <strong>Callback</strong> approach offers a more deterministic, programmatic check that is less reliant on the LLM&rsquo;s tool-calling ability, making it ideal for simple state-based exit conditions.</li>
</ul>
<h3 id="dont-forget-the-safety-net">Don&rsquo;t forget the safety net!</h3>
<p>Regardless of the pattern you choose, you must always include a <code>maxIterations</code> limit.
This is a critical safety net to prevent infinite loops if the agents get stuck in a cycle of corrections.</p>
<h2 id="conclusion-the-power-of-composition">Conclusion: The power of composition</h2>
<p>Across this four-part series, we&rsquo;ve seen how the ADK for Java provides a powerful and elegant toolkit for building sophisticated AI systems.
We&rsquo;ve moved beyond single, monolithic agents to composing workflows that are flexible, ordered, efficient, and even iterative.</p>
<ul>
<li><strong>Sub-agents</strong> brings flexibility and grants agents agency.</li>
<li><strong>Sequential agents</strong> give us order.</li>
<li><strong>Parallel agents</strong> give us speed.</li>
<li><strong>Loop agents</strong> give us intelligence through iteration.</li>
</ul>
<p>The real magic happens when you realize these aren&rsquo;t mutually exclusive choices.
They are composable building blocks.
By combining these patterns, you can design and build truly advanced agentic applications that can tackle complex, multi-step problems in a robust and maintainable way.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Mastering agentic workflows with ADK for Java: Parallel agents</title><link>https://glaforge.dev/posts/2025/07/25/mastering-agentic-workflows-with-adk-parallel-agent/</link><pubDate>Fri, 25 Jul 2025 10:20:33 +0200</pubDate><guid>https://glaforge.dev/posts/2025/07/25/mastering-agentic-workflows-with-adk-parallel-agent/</guid><description>&lt;p>Let&amp;rsquo;s continue our exploration of &lt;a href="https://github.com/google/adk-java">ADK&lt;/a> for Java (Agent Development Kit for building AI agents).
In this series, we&amp;rsquo;ve explored two fundamental agentic workflows:&lt;/p>
&lt;ul>
&lt;li>First, we used &lt;a href="https://glaforge.dev/posts/2025/07/23/mastering-agentic-workflows-with-adk-sub-agents/">&lt;code>LlmAgent&lt;/code> with sub-agents&lt;/a> to create a flexible, manager-and-specialists model.&lt;/li>
&lt;li>Then, we used the &lt;a href="https://glaforge.dev/posts/2025/07/24/mastering-agentic-workflows-with-adk-sequential-agent/">&lt;code>SequentialAgent&lt;/code>&lt;/a> to enforce a strict, linear order of operations.&lt;/li>
&lt;/ul>
&lt;p>But what if your problem isn&amp;rsquo;t about flexibility or a fixed sequence? What if it&amp;rsquo;s about &lt;strong>efficiency&lt;/strong>? Some tasks don&amp;rsquo;t depend on each other and can be done at the same time. Why wait for one to finish before starting the next?&lt;/p></description><content:encoded>
<![CDATA[<p>Let&rsquo;s continue our exploration of <a href="https://github.com/google/adk-java">ADK</a> for Java (Agent Development Kit for building AI agents).
In this series, we&rsquo;ve explored two fundamental agentic workflows:</p>
<ul>
<li>First, we used <a href="https://glaforge.dev/posts/2025/07/23/mastering-agentic-workflows-with-adk-sub-agents/"><code>LlmAgent</code> with sub-agents</a> to create a flexible, manager-and-specialists model.</li>
<li>Then, we used the <a href="https://glaforge.dev/posts/2025/07/24/mastering-agentic-workflows-with-adk-sequential-agent/"><code>SequentialAgent</code></a> to enforce a strict, linear order of operations.</li>
</ul>
<p>But what if your problem isn&rsquo;t about flexibility or a fixed sequence? What if it&rsquo;s about <strong>efficiency</strong>? Some tasks don&rsquo;t depend on each other and can be done at the same time. Why wait for one to finish before starting the next?</p>
<p>This is where the ADK for Java offers a third, powerful pattern: the <strong><code>ParallelAgent</code></strong>.</p>
<h2 id="maximum-efficiency-with-parallel-workflows">Maximum efficiency with parallel workflows</h2>
<p>A parallel workflow is designed to execute multiple agents concurrently. It&rsquo;s the perfect solution when you need to perform several independent tasks and want to get them done as quickly as possible. The <code>ParallelAgent</code> runs its sub-agents in separate threads, waits for all of them to complete, and then gathers the results.</p>
<p>This is ideal for:</p>
<ul>
<li><strong>Comprehensive data gathering:</strong> Running different types of searches or analyses on the same topic simultaneously.</li>
<li><strong>Independent sub-tasks:</strong> Performing unrelated actions that can happen in any order.</li>
<li><strong>Saving time:</strong> Drastically reducing the total execution time compared to running the same tasks one after another.</li>
</ul>
<h2 id="a-practical-example-the-market-researcher">A practical example: the <code>market-researcher</code></h2>
<p>Let&rsquo;s look at our <code>ParallelFlow.java</code> example, illustrated by the below diagram:</p>
<p><figure>
  <a href="#img-07630358efad7de7d44ddc88928f5c51">
    <img src="/img/adk/financial-report.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-07630358efad7de7d44ddc88928f5c51">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/adk/financial-report.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Here&rsquo;s the full source code, but we&rsquo;ll explore each part in details further down:</p>

<details>
  <summary>Click to see the full source code, before diving in</summary>
  <div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>companyProfiler<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;company-profiler&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Provides a general overview of a company.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to provide a brief overview of the
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        given company.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Include its mission, headquarters, and current CEO.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Use the Google Search Tool to find this information.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GoogleSearchTool())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">outputKey</span>(<span style="color:#4070a0">&#34;profile&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>newsFinder<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;news-finder&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Finds the latest news about a company.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to find the top 3-4 recent news headlines
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        for the given company.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Use the Google Search Tool.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Present the results as a simple bulleted list.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GoogleSearchTool())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">outputKey</span>(<span style="color:#4070a0">&#34;news&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>financialAnalyst<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;financial-analyst&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Analyzes the financial performance of a company.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to provide a snapshot of the given company&#39;s
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        recent financial performance.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Focus on stock trends or recent earnings reports.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Use the Google Search Tool.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GoogleSearchTool())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">outputKey</span>(<span style="color:#4070a0">&#34;financials&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>marketResearcher<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ParallelAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;market-researcher&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Performs comprehensive market research on a company.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">subAgents</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>companyProfiler,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>newsFinder,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>financialAnalyst<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>reportCompiler<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;report-compiler&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Compiles a final market research report.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to synthesize the provided information
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        into a coherent market research report.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Combine the company profile, latest news, and
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        financial analysis into a single, well-formatted report.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        ## Company Profile
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {profile}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        ## Latest News
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {news}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        ## Financial Snapshot
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {financials}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>SequentialAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;company-detective&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Collects various market information about a company.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">subAgents</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>marketResearcher,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>reportCompiler<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>).<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div></details>

<p>Now let&rsquo;s zoom in.
We&rsquo;ve built a <code>market-researcher</code> designed to quickly compile a report on a public company.
The research involves several distinct tasks that don&rsquo;t depend on each other:</p>
<ul>
<li><strong><code>company-profiler</code></strong>: Finds the company&rsquo;s mission, CEO, headquarter, etc.</li>
<li><strong><code>news-finder</code></strong>: Scans for recent news headlines.</li>
<li><strong><code>financial-analyst</code></strong>: Looks up stock performance.</li>
</ul>
<p>Those agents are taking advantage of the <code>GoogleSearchTool</code> to find up-to-date information, beyond LLM&rsquo;s training cut-off date.</p>
<p>We can run all three of these at the same time. Each agent is configured with an <code>outputKey</code> to store its findings, just like we saw with the <code>SequentialAgent</code>.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>companyProfiler<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;company-profiler&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">outputKey</span>(<span style="color:#4070a0">&#34;profile&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>newsFinder<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;news-finder&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">outputKey</span>(<span style="color:#4070a0">&#34;news&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>financialAnalyst<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;financial-analyst&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">outputKey</span>(<span style="color:#4070a0">&#34;financials&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="the-best-of-both-worlds-a-hybrid-sequential-parallel-flow">The best of both worlds: a hybrid sequential-parallel flow</h2>
<p>Now, here&rsquo;s where we take our architecture to the next level.
We&rsquo;re composing different flows, by <strong>embedding our <code>ParallelAgent</code> inside a <code>SequentialAgent</code></strong>.</p>
<p>This creates a multi-stage pipeline that combines the efficiency of parallel execution with the structured order of a sequence.</p>
<p>Here&rsquo;s how our hybrid <code>company-detective</code> works.
It&rsquo;s a <code>SequentialAgent</code> agent.
It will trigger the parallel flow, and once it completes, it will compile the final report from the output of the parallel agents.</p>
<p>The sequential agent combines two steps:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>SequentialAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;company-detective&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Collects various market information about a company.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">subAgents</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>marketResearcher,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>reportCompiler<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>).<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><ol>
<li>
<p><strong>Step 1 (in parallel):</strong> Sub-agents are collecting information in parallel about the company, the latest news, and its financial status.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>marketResearcher<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ParallelAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;market-researcher&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Performs comprehensive market research on a company.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">subAgents</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>companyProfiler,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>newsFinder,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>financialAnalyst<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>This agent&rsquo;s only job is to execute the three research tasks concurrently.
Once all three are done, it bundles their outputs (<code>profile</code>, <code>news</code>, <code>financials</code>) and passes them to the next stage in the sequence, thanks to <code>outputKey</code>s.</p>
</li>
<li>
<p><strong>Step 2 (sequentially):</strong> A final <code>report-compiler</code> agent runs.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>reportCompiler<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;report-compiler&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to synthesize the provided information...
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        ## Company Profile
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {profile}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        ## Latest News
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {news}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        ## Financial Snapshot
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {financials}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>This agent is a standard step in our <code>SequentialAgent</code>. It takes the collected data from the completed parallel step and formats it into a clean, final report.</p>
</li>
</ol>
<p>By structuring our flow this way, we&rsquo;ve created a highly modular and extensible pipeline. The parallel research block is a self-contained, reusable component. We could easily add more sequential steps before or after it, like a step to verify the company&rsquo;s stock ticker first, or a final step to email the report.</p>
<h2 id="example-run">Example run</h2>
<p>I asked my composite agent: <code>Give me a report about Google</code>.
It ran through the various agents, and generated the following report about the company:</p>

<details>
  <summary>Click to read the full report</summary>
  <pre tabindex="0"><code>## Google: Market Research Report (Q2 2025)

Executive Summary:

This report provides an overview of Google, its recent performance,
and current market position. Google, a subsidiary of Alphabet Inc.,
continues to be a dominant force in the technology industry, driven
by its core search engine, cloud services, and advancements in
artificial intelligence. Recent financial results for Q2 2025
exceeded expectations, demonstrating strong growth across key
segments. However, Google faces ongoing antitrust scrutiny and
reputational challenges.

1. Company Profile:

*   Name: Google (a subsidiary of Alphabet Inc.)
*   Mission: To organize the world&#39;s information and make it
    universally accessible and useful.
*   Headquarters: 1600 Amphitheatre Parkway, Mountain View,
    California (Googleplex)
*   CEO: Sundar Pichai (also CEO of Alphabet Inc.)

2. Latest News &amp; Developments:

*   Antitrust Lawsuit: Illinois has joined an antitrust lawsuit
    accusing Google of monopolizing advertising technology, adding
    to the legal challenges faced by the company.
*   Antitrust Violation: A federal judge ruled that Google violated
    antitrust laws, further highlighting regulatory pressures.
*   Google Maps Update: Google Maps is now displaying the Gulf of
    Mexico as the &#34;Gulf of America&#34; for U.S. users, sparking some
    controversy and discussion.
*   BadBox 2.0 Botnet: Google is taking legal action against the
    BadBox 2.0 botnet, demonstrating its commitment to cybersecurity
    and protecting its users.

3. Financial Analysis (Q2 2025):

Alphabet Inc. reported strong financial results for the second
quarter of 2025, exceeding analysts&#39; expectations.

*   Revenue: Consolidated revenue increased by 14% year-over-year
    to $96.4 billion (13% increase in constant currency).
    *   Google Services revenue increased by 12% to $82.5 billion,
        driven by Google Search, YouTube ads, and Google
        subscriptions.
    *   Google Cloud revenue increased by 32% to $13.6 billion,
        fueled by growth in Google Cloud Platform (GCP), AI
        Infrastructure, and Generative AI Solutions. The annual
        revenue run-rate for Google Cloud now exceeds $50 billion.
*   Operating Income: Increased by 14%, with an operating margin
    of 32.4%.
*   Net Income: Increased by 19%, and Earnings Per Share (EPS)
    increased by 22% to $2.31.
*   Capital Expenditures: Increased investment to approximately
    $85 billion in 2025 due to strong demand for cloud products
    and services.

4. Stock Performance &amp; Trends:

*   Stock Performance: Alphabet&#39;s Class A shares initially fell
    slightly in after-hours trading but then rose by 2%.
    The stock was up about 1% for 2025 through Wednesday&#39;s close.
*   Analyst Ratings: The average rating for GOOG stock is &#34;Buy,&#34;
    with a 12-month stock price target of $206.35, representing a
    potential increase of 7.75%.
*   Recent Stock Movement: GOOG stock has risen by 3.96% compared
    to the previous week and 14.52% over the last month.
*   52-Week Range: $140.53 (low) to $207.05 (high).
*   AI Impact: Google&#39;s AI Overview tool has over 2 billion monthly
    users, boosting search impressions by 49% since launch.
*   Long-Term Growth: An investment of $1,000 in Alphabet stock at
    its IPO in 2004 would be worth approximately $75,272 today,
    reflecting a compound annual growth rate of 22.92% over 21 years.

5. Key Takeaways &amp; Future Outlook:

*   AI as a Growth Driver: Sundar Pichai emphasizes that AI is
    positively impacting every part of the business and driving
    strong momentum. Google&#39;s advancements in AI are expected to
    continue to be a significant growth driver.
*   Cloud Services Expansion: The strong growth in Google Cloud
    revenue signifies its increasing competitiveness in the cloud
    computing market, challenging leaders like AWS and Azure.
    Increased capital expenditure in this area further indicates
    Google&#39;s commitment to expanding its cloud infrastructure.
*   Financial Strength: Alphabet&#39;s Q2 2025 results demonstrate
    its robust financial health and ability to generate significant
    revenue and profit.
*   Potential Challenges: Ongoing antitrust lawsuits and scrutiny
    pose risks to Google&#39;s market dominance and could potentially
    lead to changes in its business practices. Public perception
    issues, such as those arising from the Google Maps update,
    require careful management.

6. Conclusion:

Google remains a powerful and influential company with a strong
financial foundation and a commitment to innovation. Its investments
in AI and cloud computing position it well for future growth.
However, the company must navigate regulatory challenges and manage
its public image effectively to maintain its market leadership.
The overall outlook for Google appears positive, contingent on
its ability to adapt to evolving market dynamics and regulatory
pressures.
</code></pre></details>

<h2 id="choosing-the-right-workflow">Choosing the right workflow</h2>
<p>Let&rsquo;s quickly recap the three patterns we&rsquo;ve covered:</p>
<ul>
<li><strong><code>LlmAgent</code> with sub-agents:</strong> Choose this for <strong>flexibility</strong>. The orchestrator LLM decides the best next step. It&rsquo;s great for conversational bots and user-driven tasks.</li>
<li><strong><code>SequentialAgent</code>:</strong> Choose this for <strong>order</strong>. The process is fixed and predictable. It&rsquo;s perfect for automating linear, multi-step procedures.</li>
<li><strong><code>ParallelAgent</code>:</strong> Choose this for <strong>efficiency</strong>. Independent tasks run concurrently. It&rsquo;s ideal for data gathering and speeding up workflows.</li>
</ul>
<p>And as we&rsquo;ve just seen, the true power comes from <strong>composing these patterns together</strong> to build sophisticated, real-world applications.</p>
<p>In the final article of this series, we&rsquo;ll explore one more fascinating workflow: the <strong>loop flow</strong>, designed for tasks that require iteration and self-correction.
Stay tuned!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Mastering agentic workflows with ADK for Java: Sequential agents</title><link>https://glaforge.dev/posts/2025/07/24/mastering-agentic-workflows-with-adk-sequential-agent/</link><pubDate>Thu, 24 Jul 2025 08:58:00 +0200</pubDate><guid>https://glaforge.dev/posts/2025/07/24/mastering-agentic-workflows-with-adk-sequential-agent/</guid><description>&lt;p>In the &lt;a href="https://glaforge.dev/posts/2025/07/23/mastering-agentic-workflows-with-adk-sub-agents/">first part of this series&lt;/a>, we explored the &lt;em>&amp;ldquo;divide and conquer&amp;rdquo;&lt;/em> strategy using &lt;strong>sub-agents&lt;/strong> to create a flexible, modular team of AI specialists. This is perfect for situations where the user is in the driver&amp;rsquo;s seat, directing the flow of conversation. But what about when the &lt;em>process&lt;/em> itself needs to be in charge?&lt;/p>
&lt;p>Some tasks are inherently linear. You have to do Step A before Step B, and Step B before Step C. Think about a CI/CD pipeline: you build, then you test, then you deploy. You can&amp;rsquo;t do it out of order&amp;hellip; or if you do, be prepared for havoc!&lt;/p></description><content:encoded>
<![CDATA[<p>In the <a href="https://glaforge.dev/posts/2025/07/23/mastering-agentic-workflows-with-adk-sub-agents/">first part of this series</a>, we explored the <em>&ldquo;divide and conquer&rdquo;</em> strategy using <strong>sub-agents</strong> to create a flexible, modular team of AI specialists. This is perfect for situations where the user is in the driver&rsquo;s seat, directing the flow of conversation. But what about when the <em>process</em> itself needs to be in charge?</p>
<p>Some tasks are inherently linear. You have to do Step A before Step B, and Step B before Step C. Think about a CI/CD pipeline: you build, then you test, then you deploy. You can&rsquo;t do it out of order&hellip; or if you do, be prepared for havoc!</p>
<p>For these situations, the <a href="https://github.com/google/adk-java">ADK for Java</a> provides another powerful tool in our orchestration toolbox: the <strong><code>SequentialAgent</code></strong>.</p>
<h2 id="when-order-matters-the-sequential-workflow">When order matters: the sequential workflow</h2>
<p>A sequential workflow is all about creating a predictable, step-by-step process. Unlike the flexible sub-agent model where an orchestrator LLM decides which sub-agent to invoke and when, a <code>SequentialAgent</code> executes a predefined list of agents in a fixed order.</p>
<p>This is useful for:</p>
<ul>
<li><strong>Automating multi-step processes:</strong> Guiding a user through a fixed procedure, like a trip planner or a complex data analysis pipeline.</li>
<li><strong>Ensuring consistency:</strong> Guaranteeing that tasks are always performed in the correct sequence for reliable and predictable outcomes.</li>
<li><strong>Building on previous results:</strong> Creating workflows where the output of one step is the essential input for the next.</li>
</ul>
<h2 id="a-practical-example-a-trip-planner">A practical example: a <code>trip-planner</code></h2>
<p>Let&rsquo;s dive into our <code>SequentialFlow.java</code> example (code further down). This code defines a <code>trip-planner</code> agent that guides a user through the process of planning a vacation. You can&rsquo;t suggest restaurants before you have an itinerary, and you can&rsquo;t build an itinerary before you&rsquo;ve researched the destination. This is a perfect use case for a <code>SequentialAgent</code>.</p>
<p>Our <code>trip-planner</code> consists of three specialist agents that will be executed in a specific order:</p>
<ol>
<li><strong><code>destination-researcher</code></strong>: The first step. It takes a user&rsquo;s request (e.g., <em>&ldquo;a romantic weekend in Paris&rdquo;</em>) and finds suitable attractions.</li>
<li><strong><code>itinerary-creator</code></strong>: The second step. It takes the list of attractions from the previous step and organizes it into a logical 2-day plan.</li>
<li><strong><code>restaurant-suggester</code></strong>: The final step. It takes the completed itinerary and enriches it with relevant lunch and dinner recommendations.</li>
</ol>
<p>I&rsquo;ll show you the full source code here first, but then in subsequent sections, we&rsquo;ll explore each steps in more detail.</p>

<details>
  <summary>Click to see the full source code, before diving in</summary>
  <div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>destinationResearcher<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;destination-researcher&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Finds points of interest for a given destination
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        and travel style.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to find points of interest for a given
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        city and travel style, according to the duration of
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        the trip, and potentially budget.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Use the Google Search Tool to find relevant information.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Present the results as a simple bullet point list
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        of key attractions.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GoogleSearchTool())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">outputKey</span>(<span style="color:#4070a0">&#34;destination-research&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>itineraryCreator<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;itinerary-creator&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Creates an itinerary from a list of attractions.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to create a logical itinerary
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        from the provided list of attractions:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {destination-research}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Group the attractions by proximity or theme for each day.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Present the itinerary clearly, with each day&#39;s plan laid out.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">outputKey</span>(<span style="color:#4070a0">&#34;itinerary&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>restaurantSuggester<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;restaurant-suggester&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Suggests restaurants for each day of the itinerary.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to suggest one lunch and one dinner restaurant
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        for each day of the itinerary:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {itinerary}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Use the Google Search Tool to find restaurants
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        that are near the day&#39;s activities
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        and match the overall travel style.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Add the restaurant suggestions to the itinerary.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GoogleSearchTool())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>SequentialAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;trip-planner&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Helps you plan a trip by finding attractions,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        creating an itinerary, and suggesting restaurants.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">subAgents</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>destinationResearcher,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>itineraryCreator,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>restaurantSuggester<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div></details>

<h2 id="the-magic--of-state-passing-outputkey">The magic ✨ of state passing: <code>outputKey</code></h2>
<p><em>&ldquo;But wait,&rdquo;</em> you ask, <em>&ldquo;if these are separate agents, how does the <code>itinerary-creator</code> know what the <code>destination-researcher</code> found?&rdquo;</em></p>
<p>This is the core mechanism of the <code>SequentialAgent</code>: <strong>passing state between agents</strong>. The ADK makes this incredibly simple and elegant using an <code>outputKey</code>.</p>
<p>Here’s how it works:</p>
<ol>
<li>
<p><strong>The <em>producer</em> agent:</strong> The agent that generates the data uses the <code>.outputKey(&quot;some-key&quot;)</code> method. This tells the <code>SequentialAgent</code> to store the final result of this agent&rsquo;s turn in a context variable named <code>some-key</code>.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>destinationResearcher<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;destination-researcher&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ... instructions ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">outputKey</span>(<span style="color:#4070a0">&#34;destination-research&#34;</span>)<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// &lt;-- Produces the data</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div></li>
<li>
<p><strong>The <em>consumer</em> agent:</strong> The next agent in the sequence can then reference this stored data directly in its instructions using curly braces: <code>{some-key}</code>.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>itineraryCreator<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;itinerary-creator&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to create a logical itinerary
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        from the provided list of attractions:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {destination-research} // &lt;-- Consumes the data
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Group the attractions by proximity or theme for each day.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">outputKey</span>(<span style="color:#4070a0">&#34;itinerary&#34;</span>)<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// Can produce data for the next agent!</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div></li>
</ol>
<p>The ADK handles the injection automatically. The <code>{destination-research}</code> placeholder will be replaced by the full output of the <code>destinationResearcher</code> agent before the prompt is sent to the <code>itineraryCreator</code> agent.</p>
<h2 id="building-the-sequentialagent">Building the <code>SequentialAgent</code></h2>
<p>Putting it all together is straightforward. We use the <code>SequentialAgent.builder()</code> and provide our agents to the <code>.subAgents()</code> method.
The key difference here is that <strong>the order is strictly enforced</strong>.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>SequentialAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;trip-planner&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Helps you plan a trip by finding attractions,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        creating an itinerary, and suggesting restaurants.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">subAgents</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>destinationResearcher,<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// will always run first</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>itineraryCreator,<span style="color:#bbb">      </span><span style="color:#60a0b0;font-style:italic">// will always run second</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>restaurantSuggester<span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// will always run third</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>When this agent runs, it will execute <code>destinationResearcher</code>, take its output, feed it into <code>itineraryCreator</code>, take <em>its</em> output, feed it into <code>restaurantSuggester</code>, and finally return the completed, enriched itinerary to the user.
The last agent could also reference the output of the first agent if needed, for full context awareness!</p>
<h2 id="example-run">Example run</h2>

<details>
  <summary>Click to see the the output of a full run</summary>
  <p>Ideally, you would have a first agent collecting key information like the destination, number of days of the stay, the budget you plan to spend, the types of attractions you&rsquo;d like to visit, and the sort of cuisine you want to eat.
Here, I&rsquo;m going straight with the full detailed request:</p>
<pre tabindex="0"><code>👤 You &gt; Plan a trip of 5 days in Paris, with a medium budget,
to visit the key tourist attractions (museums, parks, landmarks),
and eat French cuisine!
</code></pre><p>Let&rsquo;s see what the destination research replies:</p>
<pre tabindex="0"><code>🤖 destination-researcher &gt;

Okay, I can help you plan a 5-day trip to Paris focusing on key
tourist attractions, French cuisine, and keeping it within a medium
budget. Here are some ideas:

Here is a possible itinerary for your 5-day trip to Paris:

Attractions:

*   Eiffel Tower: A must-see iconic landmark. Consider booking timed
    tickets in advance to avoid long queues. You can climb to the
    second floor or the top for panoramic views.
*   Louvre Museum: One of the world&#39;s largest and most visited
    museums, home to masterpieces like the Mona Lisa. Booking a time
    slot in advance is essential.
*   Arc de Triomphe: Climb to the top of the Arc for stunning views
    of the Champs-Élysées and the city.
*   Notre Dame Cathedral: While still undergoing restoration, you can
    view the cathedral from the outside and visit the archeological
    crypt.
*   Seine River Cruise: A relaxing way to see many of Paris&#39;s famous
    landmarks from a different perspective. Many cruises depart
    near the Eiffel Tower.
*   Musée d&#39;Orsay: Housed in a former railway station, this museum
    features Impressionist and Post-Impressionist art.
*   Palace of Versailles: A former royal residence, this opulent
    palace and its gardens are a must-see. Advance booking is required.
*   Sainte-Chapelle: Known for its stunning stained glass windows.
    Booking a timeslot in advance is recommended.
*   Montmartre &amp; Sacré-Cœur Basilica: Explore the artistic neighborhood
    of Montmartre and visit the Sacré-Cœur Basilica for panoramic views.

Parks &amp; Gardens:

*   Luxembourg Gardens: A beautiful and popular park with gardens,
    fountains, and activities.
*   Tuileries Garden: Located between the Louvre and Place de la
    Concorde, this formal garden is perfect for a stroll.
*   Parc des Buttes-Chaumont: One of the most beautiful and unique
    gardens in Paris, with an artificial mountain structure and
    impressive panoramic views.
*   Parc Monceau: An English-style garden with follies, statues,
    and children&#39;s play areas.

Food:

*   Bouillon Chartier: Offers traditional French cuisine
    at affordable prices.
*   Chez Gladines: Serves hearty Basque-inspired cuisine
    at reasonable prices.
*   Le Bistrot des Vosges: A no-frills restaurant with classic
    French dishes at affordable prices.
*   Brasserie Dubillot: An all-day spot for affordable
    French classics.
*   Explore local markets: Visit Rue Montorgueil
    for a taste of local market life.

Possible Itinerary Outline:

*   Day 1: Eiffel Tower, Champ de Mars, Seine River cruise,
           Arc de Triomphe, Champs-Élysées.
*   Day 2: Louvre Museum, Tuileries Garden, Place de la Concorde,
           Sainte-Chapelle, Notre Dame (exterior).
*   Day 3: Montmartre (Sacré-Cœur Basilica, Place du Tertre),
           Moulin Rouge (exterior), explore the neighborhood.
*   Day 4: Versailles Palace and Gardens (allow a full day).
*   Day 5: Musée d&#39;Orsay, Luxembourg Gardens,
           explore the Latin Quarter.

Tips for a Medium Budget:

*   Paris Museum Pass: Consider purchasing a Paris Museum Pass
    for access to over 50 museums and monuments.
    A 4-day pass might be a good option.
*   Free museum days: Some museums offer free entry on the first
    Sunday of the month, but be prepared for crowds.
*   Lunch menus: Many restaurants offer affordable fixed-price
    lunch menus.
*   Picnics: Enjoy picnics in the parks and gardens
    to save money on meals.
*   Transportation: Use the efficient
    and affordable Paris Metro system.
*   Accommodation: Consider staying in areas slightly outside
    the city center for more affordable options.
*   Look for &#34;bouillons&#34;: These restaurants serve traditional
    French food at reasonable prices.

Enjoy your trip to Paris!
</code></pre><p>It found some good suggestions for the duration of the trip, and does so considering the allocated budget.</p>
<p>Now let&rsquo;s see what the itinerary agent comes up with:</p>
<pre tabindex="0"><code>🤖 itineraryCreator &gt;

Okay, here is a possible 5-day Paris itinerary with a medium budget,
focusing on key attractions, French cuisine,
and efficient use of time:

Day 1: Iconic Paris &amp; Latin Quarter Charm

*   Morning (9:00 AM): Eiffel Tower. Pre-book tickets online to
    avoid long queues. Ascend to the second floor for stunning
    panoramic views. Budget ~€18-€29 (depending on lift access).
*   Lunch (12:00 PM): Enjoy a classic French crepe from a street
    vendor near the Eiffel Tower. Budget ~€8-€12.
*   Afternoon (1:30 PM): Champ de Mars &amp; Musée du Quai Branly -
    Jacques Chirac. Relax in the park with views of the tower.
    Then visit the museum dedicated to non-European art and cultures.
    Museum Budget: ~€12.
*   Late Afternoon (4:30 PM): Walk or take the Metro to the
    Latin Quarter. Explore the historic Sorbonne University and
    browse the Shakespeare and Company bookstore.
    Dinner (7:30 PM): Enjoy a traditional French meal at a bistro
    in the Latin Quarter. Look for set menus (formules) for better
    value. Try a steak frites or moules marinières*. Budget ~€20-€30.

Day 2: Art, Gardens &amp; Montmartre Magic

*   Morning (9:00 AM): Musée du Louvre. Focus on key masterpieces
    like the Mona Lisa, Venus de Milo, and Winged Victory
    of Samothrace. Pre-book tickets! Budget: ~€17.
    Lunch (12:30 PM): Grab a quick and tasty sandwich jambon-beurre
    (ham and butter sandwich) from a boulangerie* near the Louvre.
    Budget ~€5-€8.
*   Afternoon (2:00 PM): Tuileries Garden. Stroll through the
    beautiful gardens connecting the Louvre to Place de la Concorde.
*   Late Afternoon (4:00 PM): Place de la Concorde &amp; Champs-Élysées.
    Admire the obelisk and walk along the famous avenue towards the
    Arc de Triomphe.
*   Evening (6:30 PM): Take the Metro to Montmartre. Explore the
    charming streets, visit the Sacré-Cœur Basilica, and enjoy the
    artistic atmosphere of Place du Tertre.
    Dinner (8:00 PM): Have dinner at a traditional bistro in
    Montmartre. Consider soupe à l&#39;oignon gratinée* (French onion soup).
    Budget ~€20-€30.

Day 3: History, Notre Dame &amp; Parisian Elegance

*   Morning (9:30 AM): Île de la Cité. Visit the Conciergerie
    (former royal palace and prison) and Sainte-Chapelle
    (stunning stained glass windows).
    Budget: Conciergerie ~€11.50, Sainte-Chapelle ~€11.50.
*   Lunch (12:30 PM): Enjoy a picnic lunch by the Seine River,
    with bread, cheese, and fruit from a local market. Budget ~€10-€15.
*   Afternoon (2:00 PM): Notre Dame Cathedral. View the cathedral
    from the outside (still under renovation but an important landmark).
    Walk around Île Saint-Louis and try Berthillon ice cream.
*   Late Afternoon (4:00 PM): Le Marais District. Explore the trendy
    Marais district, known for its Jewish heritage, art galleries,
    and boutiques. Visit Place des Vosges, the oldest planned square
    in Paris.
*   Dinner (7:30 PM): Have dinner in Le Marais. This area offers
    diverse cuisine options. Consider falafel or a crêperie. Budget ~€15-€25.

Day 4: Versailles Grandeur

*   Full Day (9:00 AM - 5:00 PM): Palace of Versailles.
    Take a train (RER C) to Versailles (approx. 45 minutes).
    Explore the palace, gardens, and Marie Antoinette&#39;s estate
    (the Petit Trianon and the Queen&#39;s Hamlet).
    Pre-book tickets! Palace and Estate of Trianon ticket ~€21.50.
*   Lunch (1:00 PM): Have lunch at one of the cafes in the
    Versailles gardens, or pack your own picnic to save money.
    Budget ~€10-€20.
*   Dinner (8:00 PM): Upon return to Paris,
    have dinner near your accommodation.

Day 5: Museums and Departure

*   Morning (9:30 AM): Musée d&#39;Orsay. Located in a
    former train station,
    this museum houses a stunning collection of Impressionist
    and Post-Impressionist art. Budget ~€16.
    Lunch (12:30 PM): Enjoy a final French lunch at a brasserie
    near the Musée d&#39;Orsay. Try a croque monsieur or salade niçoise*.
    Budget ~€15-€25.
*   Afternoon (2:00 PM): Depending on your departure time,
    you can visit the Centre Pompidou for modern art (~€15),
    do some souvenir shopping, or revisit a favorite spot.
*   Departure: Allow ample time to travel to the airport
    or train station.

Budget Considerations (per person per day - estimates):

*   Accommodation: €70-€120 (hostel private room/budget hotel)
*   Food: €40-€60 (mix of affordable eateries, picnics,
    and some restaurant meals)
*   Attractions: €20-€40 (depending on museum choices
    and pre-booking)
*   Transportation: €10-€15 (Metro/bus travel - consider a
    travel pass if using public transport frequently)

Tips for a Medium Budget Trip:

*   Accommodation: Consider staying in a hostel private room,
    a budget hotel in a less central location, or an Airbnb.
*   Transportation: Utilize the Paris Metro for efficient travel.
    Consider purchasing a Navigo Découverte pass for unlimited
    travel within zones 1-5.
    Food: Take advantage of affordable lunch options like formules*
    (set menus), crêpes, and sandwiches. Shop at local markets
    for picnic supplies.
*   Museums: Take advantage of free museum days (usually the first
    Sunday of the month, but check in advance). Consider the Paris
    Museum Pass if you plan to visit many museums.
*   Free Activities: Walk along the Seine, explore parks and gardens,
    visit free churches, and enjoy the atmosphere of different
    neighborhoods.

Enjoy your trip to Paris!
</code></pre><p>Alright, it detailed the itinerary with quite precise timings.
It already includes some culinary suggestions, but the next agent will make some concrete suggestions:</p>
<pre tabindex="0"><code>🤖 &gt; restaurant-suggester

Okay, I can help you enhance your Paris itinerary with restaurant
suggestions for lunch and dinner each day, keeping your medium budget
and focus on French cuisine in mind. I will use the Google Search Tool
to find restaurants near your planned activities.

Day 1: Iconic Paris &amp; Latin Quarter Charm

*   Morning (9:00 AM): Eiffel Tower. Pre-book tickets online to avoid
    long queues. Ascend to the second floor for stunning panoramic
    views. Budget ~€18-€29 (depending on lift access).
*   Lunch (12:00 PM): Enjoy a classic French crepe from a street
    vendor near the Eiffel Tower. Budget ~€8-€12.
    Restaurant Suggestion: Crêperie Générale* (near Eiffel Tower) -
    Quick and affordable crepes with various sweet and savory options.
*   Afternoon (1:30 PM): Champ de Mars &amp; Musée du Quai Branly -
    Jacques Chirac. Relax in the park with views of the tower.
    Then visit the museum dedicated to non-European art and cultures.
    Museum Budget: ~€12.
*   Late Afternoon (4:30 PM): Walk or take the Metro to the Latin Quarter.
    Explore the historic Sorbonne University and browse the
    Shakespeare and Company bookstore.
    Dinner (7:30 PM): Enjoy a traditional French meal at a bistro in
    the Latin Quarter. Look for set menus (formules) for better value.
    Try a steak frites or moules marinières*. Budget ~€20-€30.
    Restaurant Suggestion: Le *Bouillon Chartier* (Latin Quarter/Grands
    Boulevards area) - A historic and very popular bouillon serving
    classic French dishes at very affordable prices. Expect queues.

Day 2: Art, Gardens &amp; Montmartre Magic

*   Morning (9:00 AM): Musée du Louvre. Focus on key masterpieces
    like the Mona Lisa, Venus de Milo, and Winged Victory of Samothrace.
    Pre-book tickets! Budget: ~€17.
    Lunch (12:30 PM): Grab a quick and tasty sandwich jambon-beurre
    (ham and butter sandwich) from a boulangerie* near the Louvre.
    Budget ~€5-€8.
    Restaurant Suggestion: *Boulangerie Julien* (various locations
    including near the Louvre) - Known for its traditional jambon-beurre*
    and other delicious sandwiches and pastries.
*   Afternoon (2:00 PM): Tuileries Garden. Stroll through the beautiful
    gardens connecting the Louvre to Place de la Concorde.
*   Late Afternoon (4:00 PM): Place de la Concorde &amp; Champs-Élysées.
    Admire the obelisk and walk along the famous avenue towards the
    Arc de Triomphe.
*   Evening (6:30 PM): Take the Metro to Montmartre.
    Explore the charming streets, visit the Sacré-Cœur Basilica,
    and enjoy the artistic atmosphere of Place du Tertre.
    Dinner (8:00 PM): Have dinner at a traditional bistro in Montmartre.
    Consider soupe à l&#39;oignon gratinée* (French onion soup).
    Budget ~€20-€30.
    Restaurant Suggestion: Le *Relais Gascon* (Montmartre) -
    Hearty Southwestern French cuisine, known for its generous portions
    and reasonable prices.

Day 3: History, Notre Dame &amp; Parisian Elegance

*   Morning (9:30 AM): Île de la Cité. Visit the Conciergerie
    (former royal palace and prison) and Sainte-Chapelle
    (stunning stained glass windows).
    Budget: Conciergerie ~€11.50, Sainte-Chapelle ~€11.50.
*   Lunch (12:30 PM): Enjoy a picnic lunch by the Seine River,
    with bread, cheese, and fruit from a local market.
    Budget ~€10-€15.
    Restaurant Suggestion (for supplies): Marché Maubert*
    (near Île de la Cité) - A lively local market where you can
    buy fresh bread, cheese, fruits, and other picnic essentials.
*   Afternoon (2:00 PM): Notre Dame Cathedral.
    View the cathedral from the outside (still under renovation
    but an important landmark). Walk around Île Saint-Louis and
    try Berthillon ice cream.
*   Late Afternoon (4:00 PM): Le Marais District. Explore the trendy
    Marais district, known for its Jewish heritage, art galleries,
    and boutiques. Visit Place des Vosges, the oldest planned square
    in Paris.
*   Dinner (7:30 PM): Have dinner in Le Marais.
    This area offers diverse cuisine options.
    Consider falafel or a crêperie. Budget ~€15-€25.
    Restaurant Suggestion: *L&#39;As du Fallafel* (Le Marais) -
    Famous for its delicious and affordable falafel sandwiches.
    Expect a queue, but it moves quickly.

Day 4: Versailles Grandeur

*   Full Day (9:00 AM - 5:00 PM): Palace of Versailles.
    Take a train (RER C) to Versailles (approx. 45 minutes).
    Explore the palace, gardens, and Marie Antoinette&#39;s estate
    (the Petit Trianon and the Queen&#39;s Hamlet).
    Pre-book tickets! Palace and Estate of Trianon ticket ~€21.50.
*   Lunch (1:00 PM): Have lunch at one of the cafes in the
    Versailles gardens or pack your own picnic to save money.
    Budget ~€10-€20.
    Restaurant Suggestion: *La Flotille* (Versailles Gardens) -
    A casual restaurant in the gardens offering salads, sandwiches,
    and hot dishes. A convenient option if you don&#39;t want to pack a picnic.
*   Dinner (8:00 PM): Upon return to Paris, have dinner near your accommodation.
    Restaurant Suggestion: This depends on the location of your accommodation.
    If you provide this, I can give a more specific recommendation.
    Otherwise, search for a local brasserie or bistro* in your neighborhood.

Day 5: Museums and Departure

*   Morning (9:30 AM): Musée d&#39;Orsay. Located in a former train station,
    this museum houses a stunning collection of Impressionist and
    Post-Impressionist art. Budget ~€16.
    Lunch (12:30 PM): Enjoy a final French lunch at a brasserie
    near the Musée d&#39;Orsay.
    Try a croque monsieur or salade niçoise*.
    Budget ~€15-€25.
    Restaurant Suggestion: *Brasserie Thoumieux* (near Musée d&#39;Orsay) -
    A classic Parisian brasserie with a stylish Art Deco interior,
    serving traditional dishes.
*   Afternoon (2:00 PM): Depending on your departure time,
    you can visit the Centre Pompidou for modern art (~€15),
    do some souvenir shopping, or revisit a favorite spot.
*   Departure: Allow ample time to travel to the airport or train station.
    Restaurant Suggestion (if near Centre Pompidou): Crêperie Beaubourg*
    (near Centre Pompidou) - A good option for a quick and affordable
    crepe before heading to the airport.
</code></pre><p>There&rsquo;s a restaurant being suggested twice, as it&rsquo;s in the same area, another one that&rsquo;s not really French cuisine,
or sometimes it&rsquo;s some small food-on-the-go suggestsions to keep the budget on target.
In some places, the LLM would have liked more precise guidance to help you make a choice.
But overall, the culinary expert gave some good suggestions!</p>
</details>

<h2 id="when-to-choose-sequentialagent">When to choose <code>SequentialAgent</code></h2>
<ul>
<li>Choose a <strong><code>SequentialAgent</code></strong> when you have a fixed, linear process where Step B logically depends on the output of Step A. It provides predictability and structure.</li>
<li>Choose an <strong><code>LlmAgent</code> with sub-agents</strong> (as in <a href="https://glaforge.dev/posts/2025/07/23/mastering-agentic-workflows-with-adk-sub-agents/">part 1</a>) when you need flexibility and want the orchestrator LLM to dynamically decide the best tool or next step based on the conversational context.</li>
</ul>
<p>By understanding both patterns, you can build incredibly robust and sophisticated AI systems tailored to the specific problem you&rsquo;re trying to solve.</p>
<p>Stay tuned for the next part of the series, where we&rsquo;ll explore how to run agents in parallel to handle multiple tasks at once!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Mastering agentic workflows with ADK for Java: Sub-agents</title><link>https://glaforge.dev/posts/2025/07/23/mastering-agentic-workflows-with-adk-sub-agents/</link><pubDate>Wed, 23 Jul 2025 18:42:56 +0200</pubDate><guid>https://glaforge.dev/posts/2025/07/23/mastering-agentic-workflows-with-adk-sub-agents/</guid><description>&lt;p>Let me come back to the &lt;a href="https://github.com/google/adk-java">Agent Development Kit&lt;/a> (ADK) for Java!
We recently discussed the many &lt;a href="https://glaforge.dev/posts/2025/06/15/expanding-ai-agent-capabilities-with-tools/">ways to expand ADK agents with tools&lt;/a>.
But today, I want to explore the multi-agentic capabilities of ADK, by talking about &lt;strong>sub-agent workflows&lt;/strong>.&lt;/p>
&lt;p>In upcoming articles in this series, we&amp;rsquo;ll also talk about sequential, parallel, and loop flows.&lt;/p>
&lt;h2 id="the-divide-and-conquer-strategy">The &amp;ldquo;divide and conquer&amp;rdquo; strategy&lt;/h2>
&lt;p>Think of building a complex application. You wouldn&amp;rsquo;t put all your logic in a single, monolithic class, would you? You&amp;rsquo;d break it down into smaller, specialized components. The sub-agent workflow applies this same &amp;ldquo;divide and conquer&amp;rdquo; principle to AI agents.&lt;/p></description><content:encoded>
<![CDATA[<p>Let me come back to the <a href="https://github.com/google/adk-java">Agent Development Kit</a> (ADK) for Java!
We recently discussed the many <a href="https://glaforge.dev/posts/2025/06/15/expanding-ai-agent-capabilities-with-tools/">ways to expand ADK agents with tools</a>.
But today, I want to explore the multi-agentic capabilities of ADK, by talking about <strong>sub-agent workflows</strong>.</p>
<p>In upcoming articles in this series, we&rsquo;ll also talk about sequential, parallel, and loop flows.</p>
<h2 id="the-divide-and-conquer-strategy">The &ldquo;divide and conquer&rdquo; strategy</h2>
<p>Think of building a complex application. You wouldn&rsquo;t put all your logic in a single, monolithic class, would you? You&rsquo;d break it down into smaller, specialized components. The sub-agent workflow applies this same &ldquo;divide and conquer&rdquo; principle to AI agents.</p>
<p>Instead of one &ldquo;do-it-all&rdquo; agent, you create a hierarchy:</p>
<ul>
<li>An <strong>orchestrator agent</strong> that acts as a project manager.</li>
<li>Several <strong>specialized sub-agents</strong> that act as team members, each with a specific skill.</li>
</ul>
<p>This approach has some important advantages:</p>
<ul>
<li><strong>Clarity and focus:</strong> Each agent gets a clear, concise set of instructions (its system prompt). A focused agent is a more reliable and predictable agent. It&rsquo;s less prone to hallucinations, or being lost at the complexity of the task at hand.</li>
<li><strong>Modularity and reusability:</strong> An agent designed for a specific task, like summarizing text, can be reused across many different applications. So you can play Lego bricks to build your next agents!</li>
<li><strong>Maintainability:</strong> Debugging or enhancing a small, focused agent is infinitely easier than untangling the logic of a massive, overburdened one, with potentially conflicting system instructions to tackle all the corner cases of each situation.</li>
</ul>
<h2 id="a-practical-example-a-_content-companion_">A practical example: a <em>content companion</em></h2>
<p>Let&rsquo;s make this concrete by looking at an example I&rsquo;ve been playing with:
a &ldquo;content companion,&rdquo; an AI assistant designed to help bloggers and influencers research topics and draft social media posts.</p>
<p>This system is a perfect illustration of a sub-agent hierarchy, composed of a few distinct agents working in concert.</p>

<details>
  <summary>Click to see the full source code, before diving in</summary>
  <div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>LlmAgent<span style="color:#bbb"> </span>searchAgent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;google-search-agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        An agent that searches on Google Search
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to search on Google Search.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Use the Google Search Tool to search up-to-date
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        and relevant information about the topic.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GoogleSearchTool())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>LlmAgent<span style="color:#bbb"> </span>topicSearchAgent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;topic-search-agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        An agent that searches and dives in particular topics
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to help explore a particular topic.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Use the `google-search-agent` tool to search up-to-date
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        and relevant information about the topic.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Be sure to display the result of the search
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        to inform the user.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(AgentTool.<span style="color:#4070a0">create</span>(searchAgent))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">afterAgentCallback</span>(callbackContext<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>callbackContext.<span style="color:#4070a0">eventActions</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">setTransferToAgent</span>(<span style="color:#4070a0">&#34;content-companion&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Maybe.<span style="color:#4070a0">empty</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>})<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>LlmAgent<span style="color:#bbb"> </span>socialMediaAgent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;social-media-agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        An agent that crafts social media posts about a topic
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Given content about a topic, your role is to craft
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        an attractive social media post about it.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Don&#39;t hesitate to use meaningful emojis
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        when it helps convey the message.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">afterAgentCallback</span>(callbackContext<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>callbackContext.<span style="color:#4070a0">eventActions</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">setTransferToAgent</span>(<span style="color:#4070a0">&#34;content-companion&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Maybe.<span style="color:#4070a0">empty</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>})<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;content-companion&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        A content companion that searches topics
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        and crafts compelling social media stories
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to help bloggers and influencers
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        come up with interesting topic ideas,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        to search information about the topic to write about,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        and potentially to craft a compelling social media post.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Don&#39;t search yourself:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Use the `topic-search-agent`
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        to find information about a topic.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Don&#39;t write social media posts yourself:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Use the `social-media-agent`
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        to craft a social media post about the topic.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">subAgents</span>(socialMediaAgent,<span style="color:#bbb"> </span>topicSearchAgent)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div></details>

<p>Let&rsquo;s zoom in on the various components.</p>
<h3 id="1-the-orchestrator-content-companion">1. The orchestrator: <code>content-companion</code></h3>
<p>At the top of our hierarchy is the <code>content-companion</code>. This is the agent we, the user, will interact with. Its main job is to understand our high-level goal and delegate the actual work to the right specialist.</p>
<p>Notice its instructions: it&rsquo;s explicitly told <em>what not to do</em>.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;content-companion&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        A content companion that searches topics
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        and crafts compelling social media stories
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to help bloggers and influencers
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        come up with interesting topic ideas,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        to search information about the topic to write about,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        and potentially to craft a compelling social media post.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Don&#39;t search yourself: Use the `topic-search-agent`
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        to find information about a topic.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Don&#39;t write social media posts yourself:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Use the `social-media-agent` to craft
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        a social media post about the topic.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">subAgents</span>(socialMediaAgent,<span style="color:#bbb"> </span>topicSearchAgent)<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// Sub-agents!</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>The <code>.subAgents()</code> method is where the magic happens. It registers the <code>socialMediaAgent</code> and <code>topicSearchAgent</code> (detailed further down below)
and makes them available as callable agents for the <code>content-companion</code>. The orchestrator doesn&rsquo;t need to know <em>how</em> they work, just <em>what</em> they do.</p>
<p>Now lets turn our attention to the sub-agents.</p>
<h3 id="2-the-specialists-the-sub-agents">2. The specialists: The sub-agents</h3>
<p>The <code>content-companion</code> manages two direct reports:</p>
<ul>
<li><strong><code>topic-search-agent</code></strong>: the research assistant.</li>
<li><strong><code>social-media-agent</code></strong>: the creative copywriter.</li>
</ul>
<p>Each of these is an independent <code>LlmAgent</code> with its own focused prompt. For example, the <code>social-media-agent</code> is single-mindedly focused on its creative task:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>LlmAgent<span style="color:#bbb"> </span>socialMediaAgent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;social-media-agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        An agent that crafts social media posts about a topic
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Given content about a topic, your role is to craft
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        an attractive social media post about it.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Don&#39;t hesitate to use meaningful emojis
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        when it helps convey the message.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><h3 id="3-deeper-delegation-agents-as-tools">3. Deeper delegation: Agents as tools</h3>
<p>Here&rsquo;s where it gets interesting.
The delegation doesn&rsquo;t have to stop at one level.
As I covered in my previous post on <a href="https://glaforge.dev/posts/2025/06/15/expanding-ai-agent-capabilities-with-tools/">expanding agent capabilities with tools</a>,
one agent can be used as a tool by another.</p>
<p>Our <code>topic-search-agent</code> needs to search the web, but we want to abstract that capability. So, it delegates the raw search functionality to an even more specialized agent.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// The lowest-level worker,</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// whose only job is to use the GoogleSearchTool</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>LlmAgent<span style="color:#bbb"> </span>searchAgent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;google-search-agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GoogleSearchTool())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// The mid-level manager,</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// which uses the searcher as a tool</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>LlmAgent<span style="color:#bbb"> </span>topicSearchAgent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;topic-search-agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to help explore a particular topic.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Use the `google-search-agent` tool...
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(AgentTool.<span style="color:#4070a0">create</span>(searchAgent))<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// An agent becomes a tool!</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>By wrapping <code>searchAgent</code> with <code>AgentTool.create()</code>, we turn a fully-fledged agent into a simple, callable tool. This is a powerful abstraction that keeps our agent responsibilities cleanly separated.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>In our use case, this is also necause of a technical limitation:
in ADK, with Gemini, you can&rsquo;t have multiple tools configured when a built-in tool like the <code>GoogleSearchTool</code> is declared.
So using <em>agents-as-tools</em> also helps circumventing this limitation.</p>
      </div>
    </div><h2 id="tracing-the-flow-of-delegation">Tracing the flow of delegation</h2>
<p>Let&rsquo;s trace a simple request to see how this all fits together:</p>
<ol>
<li><strong>Me:</strong> <em>&ldquo;Find me some information about the latest planet discoveries in 2025.&rdquo;</em></li>
<li><strong><code>content-companion</code>:</strong> It receives the request. Its instructions forbid it from searching. It sees that the <code>topic-search-agent</code> is the right tool for the job and invokes it.</li>
<li><strong><code>topic-search-agent</code>:</strong> It&rsquo;s now active. Its instructions tell it to use the <code>google-search-agent</code> tool. It calls this tool to perform the search.</li>
<li><strong><code>google-search-agent</code>:</strong> This agent&rsquo;s only job is to execute its tool, <code>GoogleSearchTool</code>. It runs the search and returns the raw results.</li>
<li><strong>The results flow back up the chain:</strong> The raw data goes from <code>google-search-agent</code> to <code>topic-search-agent</code>. The <code>topic-search-agent</code> might then process or summarize these results before passing them up to the <code>content-companion</code>, which finally presents the answer to me.</li>
</ol>
<p>Then via a new interaction, you can ask for a tweet about the discoveries:</p>
<ol>
<li><strong>Me:</strong> <em>&ldquo;Craft a short tweet about those discoveries.&rdquo;</em></li>
<li><strong><code>social-media-agent</code>:</strong> It receives the request, and remembers the ongoing conversation about exoplanets, and will prepare a tweet as requested.</li>
<li><strong>The result is shared with the <code>content-companion</code></strong>, which will deliver the final tweet suggestion back to me.</li>
</ol>
<h2 id="choreographing-the-cadk-onversation-with-afteragentcallback">Choreographing the cadk onversation with <code>afterAgentCallback</code></h2>
<p>If you look closely at the entire Java code posted at the beginning of the article,
you&rsquo;ll spot a curious addition to our specialist agents: an <code>afterAgentCallback</code>.
This isn&rsquo;t just boilerplate; it&rsquo;s a crucial piece of conversational choreography.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>.<span style="color:#4070a0">afterAgentCallback</span>(callbackContext<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>callbackContext.<span style="color:#4070a0">eventActions</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setTransferToAgent</span>(<span style="color:#4070a0">&#34;content-companion&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Maybe.<span style="color:#4070a0">empty</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>})<span style="color:#bbb">
</span></span></span></code></pre></div><p>So, what does this do? It explicitly manages the flow of control.</p>
<p>Without this callback, after the <code>topic-search-agent</code> finishes its research, the conversational focus would remain with it.
If I then said, <em>&ldquo;Okay, now write a tweet about that,&rdquo;</em> the <code>topic-search-agent</code> would be confused. That&rsquo;s not its job.
I&rsquo;d be <em>&ldquo;stuck&rdquo;</em> talking to the specialist, and I&rsquo;d have to manually re-engage the main <code>content-companion</code>.
That&rsquo;s a clunky user experience.</p>
<p>The <code>afterAgentCallback</code> solves this elegantly.
It acts as a <em>&ldquo;return to sender&rdquo;</em> instruction. The <code>setTransferToAgent(&quot;content-companion&quot;)</code> command tells the ADK:
<em>&ldquo;As soon as this agent&rsquo;s turn is over, immediately transfer the conversational control back to the <code>content-companion</code>.&rdquo;</em></p>
<p>This ensures the user always has a smooth, continuous dialogue with the main orchestrator, which is always ready for the next command. It&rsquo;s a vital mechanism for designing complex yet intuitive agentic systems.</p>
<h2 id="why-bother-with-agent-hierarchies">Why bother with agent hierarchies?</h2>
<p>Creating a sub-agent workflow makes the difference when:</p>
<ul>
<li>Your problem is multi-faceted, like <em>&ldquo;research a topic, write a summary, and then draft a tweet.&rdquo;</em></li>
<li>You want to build a system that is robust and easily extensible. Need a new capability? Just build a new specialist agent and register it with your orchestrator.</li>
<li>You want your prompts to be simple and effective. Less ambiguity in the prompt leads to more reliable behavior from the LLM.</li>
</ul>
<p>By composing agents this way, we move from simple <em>command-and-response</em> bots to building truly scalable and capable AI systems.</p>
<p>Stay tuned for our next post! We&rsquo;ll explore <strong>sequential workflows</strong> to orchestrate tasks that must happen in a specific, predictable order.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>The Sci-Fi naming problem: Are LLMs less creative than we think?</title><link>https://glaforge.dev/posts/2025/07/22/the-sci-fi-naming-problem-are-llms-less-creative-than-we-think/</link><pubDate>Tue, 22 Jul 2025 17:13:10 +0200</pubDate><guid>https://glaforge.dev/posts/2025/07/22/the-sci-fi-naming-problem-are-llms-less-creative-than-we-think/</guid><description>&lt;p>Like many developers, I&amp;rsquo;ve been exploring the creative potential of Large Language Models (LLMs). At the beginning of the year, I crafted a project to build an &lt;a href="https://glaforge.dev/posts/2025/01/27/an-ai-agent-to-generate-short-scifi-stories/">AI agent that could generate short science-fiction stories&lt;/a>. I used &lt;a href="https://docs.langchain4j.dev/">LangChain4j&lt;/a> to create a deterministic workflow to drive &lt;a href="https://cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/2-5-pro?utm_campaign=CDR_0x7a40493f_default_b433495891&amp;amp;utm_medium=external&amp;amp;utm_source=blog">Gemini&lt;/a> for the story generation, and &lt;a href="https://cloud.google.com/vertex-ai/generative-ai/docs/models/imagen/4-0-generate-preview-06-06?utm_campaign=CDR_0x7a40493f_default_b433495891&amp;amp;utm_medium=external&amp;amp;utm_source=blog">Imagen&lt;/a> for the illustrations. The initial results were fascinating. The model could weave narratives, describe futuristic worlds, and create characters with seemingly little effort. But as I generated more stories, a strange and familiar pattern began to emerge…&lt;/p></description><content:encoded>
<![CDATA[<p>Like many developers, I&rsquo;ve been exploring the creative potential of Large Language Models (LLMs). At the beginning of the year, I crafted a project to build an <a href="https://glaforge.dev/posts/2025/01/27/an-ai-agent-to-generate-short-scifi-stories/">AI agent that could generate short science-fiction stories</a>. I used <a href="https://docs.langchain4j.dev/">LangChain4j</a> to create a deterministic workflow to drive <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/2-5-pro?utm_campaign=CDR_0x7a40493f_default_b433495891&amp;utm_medium=external&amp;utm_source=blog">Gemini</a> for the story generation, and <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/models/imagen/4-0-generate-preview-06-06?utm_campaign=CDR_0x7a40493f_default_b433495891&amp;utm_medium=external&amp;utm_source=blog">Imagen</a> for the illustrations. The initial results were fascinating. The model could weave narratives, describe futuristic worlds, and create characters with seemingly little effort. But as I generated more stories, a strange and familiar pattern began to emerge…</p>
<p>A &ldquo;Dr. Thorne&rdquo; would frequently appear, sometimes as a brilliant scientist, other times as a starship captain. The heroines were often named Anya, or Elena. The stories unfolded on planets with names that all seemed to echo each other (but often inspired by current exoplanet findings). It was as if the AI was drawing from a very small, very specific cast of characters and settings.</p>
<p>My first thought was that this was a limitation of the specific model I was using, Google&rsquo;s Gemini. Was it simply not &ldquo;creative&rdquo; enough? Was its imagination stuck in a loop? I was about to chalk it up to a model-specific quirk, but then I stumbled upon a benchmark for long-form creative writing: <a href="https://eqbench.com/creative_writing_longform.html">EQ Bench’s longform creative writing</a>. To my surprise, I noticed that other models from different providers were also generating sci-fi stories with eerily similar character names. <strong>The problem wasn&rsquo;t isolated; it was systemic</strong>.</p>
<ul>
<li>The Gemini 2.5 Pro sci-fi <a href="https://eqbench.com/results/creative-writing-longform/gemini-2.5-pro-preview-03-25_longform_report.html">sample</a> on the benchmark had its main character called… Dr Aris Thorne.</li>
<li>DeepSeek’s <a href="https://eqbench.com/results/creative-writing-longform/deepseek-ai__DeepSeek-V3-0324_longform_report.html">novel</a> was featuring Elara Voss as its main protagonist.</li>
<li>Claude Opus 4’s <a href="https://eqbench.com/results/creative-writing-longform/claude-opus-4_longform_report.html">sample</a> was talking about Elana Vasquez. Common names I could find across models multiple times.</li>
</ul>
<p>This discovery shifted my perspective. What if the issue wasn&rsquo;t a lack of creativity, but a reflection of the data the models were trained on? The hypothesis was simple: <strong>for a specialized genre like science fiction, perhaps the available training data is more limited than we assume</strong>. If the models were all learning from a similar, relatively small pool of sci-fi literature, it would stand to reason they would reproduce the most common elements from that pool.</p>
<h1 id="hunting-for-science-fiction-datasets"><strong>Hunting for science-fiction datasets</strong></h1>
<p>This led me down a rabbit hole to <a href="https://www.kaggle.com/">Kaggle</a>, a popular platform for data scientists, where I searched for science-fiction book datasets. My intuition was that <strong>big models were trained on common datasets of novels</strong>. And for a very precise topic, the actual data contained in those dataset was probably scarce, leading to less diversity. I found exactly what I was looking for: large text corpuses of sci-fi novels. My intuition nagging at me, I began searching through these datasets for the very names that had been popping up in my generated stories.</p>
<ul>
<li>The <a href="https://www.kaggle.com/datasets/tanguypledel/science-fiction-books-subgenres">Science Fiction Books dataset</a> featured 10 thousands books, but only with metadata, but the book descriptions cover the main characters, the narrative, etc. The data is split across several CSV files for different types (or sub-genres) of sci-fi stories.</li>
<li>The <a href="https://www.kaggle.com/datasets/jannesklaas/scifi-stories-text-corpus">SciFi Stories Text corpus</a> is a ~150MB text file containing various sci-fi stories (however I’m not sure exactly what it covers and where the data was coming from).</li>
</ul>
<p>I can’t say if the big models were trained on those particular datasets, but there they were. Dr. Thorne. Elara. Anya…</p>
<p>The names weren&rsquo;t just present; they were frequent. The models weren&rsquo;t failing at being creative. They were succeeding, perhaps too well, at <strong>identifying and reproducing the most statistically common patterns in the data</strong> they were fed. The perceived lack of creativity was, in fact, a direct consequence of the limitations of the training data for this specific genre.</p>
<h1 id="gemini-cli-to-the-rescue"><strong>Gemini CLI to the rescue</strong></h1>
<p>I pointed <a href="https://blog.google/technology/developers/introducing-gemini-cli-open-source-ai-agent/?utm_campaign=CDR_0x7a40493f_default_b433495891&amp;utm_medium=external&amp;utm_source=blog">Gemini CLI</a> at the CSV files and at this big text file of sci-fi novels from the 2 datasets I mentioned, and asked it to find some references to characters&hellip;</p>
<p><figure>
  <a href="#img-3fc3b6878c66d380891dfac89d67ac98">
    <img src="/img/gemini-cli/ai-story-gemini-cli-1.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-3fc3b6878c66d380891dfac89d67ac98">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/ai-story-gemini-cli-1.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>The infamous Dr Thorne appeared 204 times in 26 book descriptions, and Anya was present in 8 book descriptions and made some 204 appearances! So they were clearly very well known!</p>
<p>After various searches through the datasets, Gemini CLI told me:</p>
<p><figure>
  <a href="#img-7e0b0684ba4f4be81180123f0b0eb942">
    <img src="/img/gemini-cli/ai-story-gemini-cli-2.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-7e0b0684ba4f4be81180123f0b0eb942">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini-cli/ai-story-gemini-cli-2.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Thorne, Anya, Althea, Elena were very busy characters in the science-fiction novels! That’s certainly why my <a href="https://glaforge.dev/posts/2025/01/27/an-ai-agent-to-generate-short-scifi-stories/">short sci-fi story generator</a> was often yielding the same characters&rsquo; names.</p>
<h1 id="conclusion"><strong>Conclusion</strong></h1>
<p>This experience reveals a crucial nuance in how we should think about AI and creativity. We often treat LLMs as black boxes of boundless imagination. But in reality, <strong>their creative output is a mirror reflecting the data they have ingested</strong>. For broad topics, where the training data is vast and diverse (the entire internet, for example), this mirror is so large and multifaceted that the reflections appear endlessly unique. But for more niche domains, like the specific subgenres of science fiction, the mirror is smaller. The reflections become more focused, more repetitive, and the patterns become obvious.</p>
<p>So, are LLMs <em>uncreative</em>? The answer is more complex than a simple yes or no. Their creativity is not one of imagination in the human sense, but of sophisticated pattern recognition and recombination. When the patterns are limited, so is the apparent creativity. This doesn&rsquo;t diminish their power as tools, but it does highlight the <strong>critical role of data diversity</strong>. For AI to be a truly powerful creative partner in specialized fields, it needs to be fed a rich and varied diet of information from that domain.</p>
<p>But it gave me some ideas for improving my story generator, to further enhance its creativity, by focusing on creating more diverse names first, regardless of the science-fiction focus (to avoid staying in the pit of the common names), building up their profiles, and only then injecting them in the context of the sci-fi world… So I may come back to this creative process in upcoming episodes on this blog!</p>
<p>Until then, we may have to get used to hearing more tales of Dr. Thorne and his adventures across the galaxy! To infinity and beyond!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>AI Agents, the New Frontier for LLMs</title><link>https://glaforge.dev/talks/2025/07/16/ai-agents-the-new-frontier-for-llms/</link><pubDate>Wed, 16 Jul 2025 12:14:22 +0200</pubDate><guid>https://glaforge.dev/talks/2025/07/16/ai-agents-the-new-frontier-for-llms/</guid><description>&lt;p>I recently gave a talk titled &lt;strong>&amp;ldquo;AI Agents, the New Frontier for LLMs&amp;rdquo;&lt;/strong>. The session explored how we can move beyond simple request-response interactions with Large Language Models to build more sophisticated and autonomous systems.&lt;/p>
&lt;p>If you&amp;rsquo;re already familiar with LLMs and Retrieval Augmented Generation (RAG), the next logical step is to understand and build AI agents.&lt;/p>
&lt;h2 id="what-makes-a-system-agentic">What makes a system &amp;ldquo;agentic&amp;rdquo;?&lt;/h2>
&lt;p>An agent is more than just a clever prompt. It’s a system that uses an LLM as its core reasoning engine to operate autonomously. The key characteristics that make a system &amp;ldquo;agentic&amp;rdquo; include:&lt;/p></description><content:encoded>
<![CDATA[<p>I recently gave a talk titled <strong>&ldquo;AI Agents, the New Frontier for LLMs&rdquo;</strong>. The session explored how we can move beyond simple request-response interactions with Large Language Models to build more sophisticated and autonomous systems.</p>
<p>If you&rsquo;re already familiar with LLMs and Retrieval Augmented Generation (RAG), the next logical step is to understand and build AI agents.</p>
<h2 id="what-makes-a-system-agentic">What makes a system &ldquo;agentic&rdquo;?</h2>
<p>An agent is more than just a clever prompt. It’s a system that uses an LLM as its core reasoning engine to operate autonomously. The key characteristics that make a system &ldquo;agentic&rdquo; include:</p>
<ul>
<li><strong>Planning and decomposition</strong>: The ability to break down a complex goal into a sequence of smaller, manageable steps.</li>
<li><strong>Tool use</strong>: The capacity to interact with external systems, APIs, or data sources to gather information or perform actions (via <em>&ldquo;function calling&rdquo;</em>). This could be anything from searching the web to querying a database or calling a specific function.</li>
<li><strong>Reflection</strong>: The capability to analyze its own actions and their outcomes, learn from mistakes, and refine its plan to achieve the final objective.</li>
</ul>
<p>This matters because it&rsquo;s a fundamental shift from simply <em>asking</em> an LLM for information to <em>tasking</em> a system with achieving a goal. An agent can handle ambiguity and orchestrate a series of operations to deliver a result that a single LLM call cannot.</p>
<h2 id="common-design-patterns"><strong>Common Design Patterns</strong></h2>
<p>To build these agents, we rely on established design patterns that provide structure to their autonomous behavior. In the talk, I cover several of these with concrete code examples, including:</p>
<ul>
<li><strong>ReAct (Reason and Act)</strong>: This is a foundational pattern where the agent iterates through a loop of reasoning about the next best action, taking that action (often with a tool), and then observing the outcome to inform its next step.</li>
<li><strong>Function calling</strong>: This allows the model to declare that it needs to invoke an external tool or function and to provide the necessary arguments. The system then executes the function and feeds the result back to the model so it can proceed.</li>
<li><strong>Human-in-the-Loop</strong>: For tasks that require validation, approval, or handling ambiguity, this pattern ensures that the agent can pause its execution and request input from a human user before continuing.</li>
</ul>
<hr />
<p>The presentation demonstrates these concepts with practical examples, including a RAG agent and a story-generation application, using frameworks like <a href="https://docs.langchain4j.dev/">LangChain4j</a> and the new <a href="https://github.com/google/adk-java">Agent Development Kit</a> (ADK, in particular ADK for Java). I also touch on emerging standards for agent-to-agent communication (via the <a href="https://a2aproject.github.io/A2A/latest/">A2A</a> protocol), and how to interact with external tools via <a href="http://modelcontextprotocol.io/">MCP</a> (the Model Context Protocol).</p>
<p>If you are interested in learning how to build systems that can reason, plan, and act, you can find the full recording (for now only in French) and the accompanying slides below.</p>
<h2 id="the-abstract">The abstract</h2>
<blockquote>
<p>Know Large Language Models at your fingertips? Mastering Retrieval Augmented Generation to help an LLM search your documents? It&rsquo;s time to dive into the wonderful world of intelligent agents, the next frontier for LLMs!</p>
<p>In this session, we will first define what agents are, or at least what makes a system &ldquo;agentic&rdquo;. We will explain the limitations of LLMs. Then, through concrete examples, we will implement different agents in Java, using the LangChain4j framework and ADK (the Agent Development Kit), to illustrate some typical agent patterns and to understand how to go beyond a simple LLM call to obtain responses that meet the needs of your users, or even to trigger actions with the surrounding system.</p>
<p>But it’s not all we’ll learn about! An agent doesn’t live alone on a desert tropical island. Indeed it can communicate with other agents via tools that can be invoked thanks to the Model Context Protocol (MCP). They can also interact with other remote agents from other platforms and ecosystems, thanks to the Agent To Agent protocol (A2A).</p>
<p>Are you ready for the next hype on agents? Come and discover it in this session!</p></blockquote>
<h2 id="the-slide-deck">The slide deck</h2>
<script async class="speakerdeck-embed" data-id="ff7e2bd87c264008bc8ca8cb8112f936" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<h2 id="the-video-of-the-talk">The video of the talk</h2>
<p>I had the chance to give this talk at <a href="https://www.devoxx.fr/agenda-2025/speaker/guillaume-laforge/">Devoxx France</a>.
The only recording I have right now in French &#x1f1eb;&#x1f1f7;, but hopefully, once this talk is available in English, I&rsquo;ll update this post to also share an English version of it.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/Yv7NX4cDxuI?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Advanced RAG — Using Gemini and long context for indexing rich documents (PDF, HTML...)</title><link>https://glaforge.dev/posts/2025/07/14/advanced-rag-using-gemini-and-long-context-for-indexing-rich-documents/</link><pubDate>Sun, 13 Jul 2025 11:25:25 +0200</pubDate><guid>https://glaforge.dev/posts/2025/07/14/advanced-rag-using-gemini-and-long-context-for-indexing-rich-documents/</guid><description>&lt;p>A very common question I get when presenting and talking about advanced RAG
(Retrieval Augmented Generation) techniques, is
&lt;strong>how to best index and search rich documents like PDF&lt;/strong> (or web pages),
that contain both text and rich elements, like pictures or diagrams.&lt;/p>
&lt;p>Another very frequent question that people ask me is about &lt;strong>RAG versus long context windows&lt;/strong>.
Indeed, models with long context windows usually have a more global understanding of a document,
and each excerpt in its overall context. But of course,
you can&amp;rsquo;t feed all the documents of your users or customers in one single augmented prompt.
Also, RAG has other advantages like offering a much lower latency, and is generally cheaper.&lt;/p></description><content:encoded>
<![CDATA[<p>A very common question I get when presenting and talking about advanced RAG
(Retrieval Augmented Generation) techniques, is
<strong>how to best index and search rich documents like PDF</strong> (or web pages),
that contain both text and rich elements, like pictures or diagrams.</p>
<p>Another very frequent question that people ask me is about <strong>RAG versus long context windows</strong>.
Indeed, models with long context windows usually have a more global understanding of a document,
and each excerpt in its overall context. But of course,
you can&rsquo;t feed all the documents of your users or customers in one single augmented prompt.
Also, RAG has other advantages like offering a much lower latency, and is generally cheaper.</p>
<p>However, the answer I usually give is that you can take the best of both worlds, with a <strong>hybrid approach</strong>:</p>
<ul>
<li>You can <strong>use a RAG approach</strong> (or a mix of keyword search engine, graph RAG, or vector-based RAG) to find relevant documents for the user query.</li>
<li>And then <strong>feed only those key documents in the context window</strong> of a model like <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/start/quickstart?usertype=adc&amp;utm_campaign=CDR_0x7a40493f_default_b431756993&amp;utm_medium=external&amp;utm_source=blog">Gemini</a> that accepts 1+ million tokens.</li>
</ul>
<p>That way, the model can focus on whole documents, with a finer understanding of each one,
but is not overwhelmed with too many documents to find the needle in the haystack.</p>
<blockquote>
<p>The current trending topic of <strong>context engineering</strong> is exactly about this:
it&rsquo;s crucial to give the best contextual information to your favorite LLM!</p></blockquote>
<h1 id="using-gemini-to-finely-index-a-rich-pdf-document-for-rag-search"><strong>Using Gemini to finely index a rich PDF document for RAG search</strong></h1>
<p>Before feeding the relevant document in the context window of the LLM, we first need to index it.</p>
<h2 id="about-hypothetical-question-generation">About hypothetical question generation</h2>
<p>In my last article, I explored an interesting technique called <a href="https://glaforge.dev/posts/2025/07/06/advanced-rag-hypothetical-question-embedding/">Hypothetical Questions embedding</a>. This technique works great with question/answer tasks, as the idea is to compare <em>&ldquo;hypothetical&rdquo;</em> questions to user questions. This tends to give higher levels of similarity when calculating the vector similarity for the semantic search.</p>
<p>One aspect I haven&rsquo;t explained in detail is how many questions we should generate for a given paragraph or set of sentences. It seemed to me that 10 questions or so per chunks of 2000 characters or less was a good rule of thumb. But if you use an LLM to parse a document, and let it figure out sections of text that make sense together, the LLM can as well figure out which questions to ask, and how many of them, depending on both the length of the given text, and its semantic density.</p>
<h2 id="what-about-diagrams-and-other-pictures">What about diagrams and other pictures?</h2>
<p>Sometimes customers I talk to manage to extract and embed the text in rich documents, however, they&rsquo;re not sure how to treat the various diagrams and images in those documents. They may also use an image model to calculate embeddings for whole pages (containing both the text and the pictures). It&rsquo;s possible to use image models to extract bounding boxes around images and then somehow extract just the boxes containing the pictures. Then they analyze the picture, get a text description, and embed that text.</p>
<p>Again, a multimodal model like Gemini is actually able to look at each page, see where all the diagrams are, and see all the images in each page, to extract some meaningful description.</p>
<h2 id="-the-idea-use-an-llm-to-chunk-text-generate-questions-and-describe-pictures">💡 The idea: Use an LLM to chunk text, generate questions, and describe pictures</h2>
<p>All at once! Let’s use Gemini to do this for us (or any multimodal LLM with a context window large enough to contain a whole document of your corpus). We’ll use structured output to be get a JSON output that you can easily parse to extract the key information (chunks, questions, image descriptions, but also pages or titles).</p>
<h1 id="the-smart-prompt"><strong>The smart prompt</strong></h1>
<p>Let’s have a look at the following prompt which we can use as system instructions for an LLM call:</p>
<pre tabindex="0"><code>Your goal is to analyze long documents for Retrieval Augmented
Generation (RAG) by splitting the content in pieces.
The content pieces will be the chunks that will be embedded
thanks to an embedding model to calculate vector embedding
to store in a vector database.

Given a long PDF document, you must extract and format the
content so that each piece of content is meaningful
as a standalone unit of text.

Each piece of content should be tagged with the page number
it appears in, and the title of the section it&#39;s situated in,
as well as a list of questions whose answers can be provided
by this piece of content.

If there is a table of content, you can use it for adding
more context to a given excerpt, or for the section titles,
but don&#39;t return the table of content as an excerpt.

When you encounter an image, a picture, or a diagram,
the piece of content you return should be the description
of the image with as much detail as possible.
If the figure has a caption, use it as the title
of the piece of content.
Like for text excerpts, use the title of the current section
the image is in in its description.

Don&#39;t create pieces of text for menu, navigation,
and other unrelated elements.
But be sure to create a piece of content for all the text
of the document.
Go through ALL the pages of the document.
The piece of text should be the exact text found in the article.
DON&#39;T change a single world. DON&#39;T summarize.
</code></pre><p>Let’s examine the prompt, but feel free to enhance or expand it for your needs:</p>
<ul>
<li>The first paragraph gives the general <strong>goal</strong> we try to achieve, and explains the general idea of the task we ask the LLM.</li>
<li>The second paragraph tells the LLM it’s important to <strong>create pieces of content</strong> (chunks!) <strong>that are meaningful</strong>. It tends to help it find relevant chunks that talk about the same topic or aspect, instead of splitting the document into random and/or fixed length spaces.</li>
<li>The third paragraph details what information to carry for each individual piece of content. We want to retain:
<ul>
<li>the <strong>page number</strong> (in particular for PDF documents),</li>
<li>the <strong>title of the section</strong> the text appears in (if it’s a structured document with various sections and header titles),</li>
<li>a <strong>list of questions</strong> in the spirit of the Hypothetical Question Embedding technique we’ve used in the previous article,</li>
<li>and of course the <strong>chunk</strong> itself.</li>
</ul>
</li>
<li>The fourth paragraph says that we don’t need to create chunks for the table of contents, but that a table of contents can actually be useful for crafting the titles of the sections.</li>
<li>The fifth paragraph now talks about diagrams, pictures, etc. It tells the LLM it should actually create a detailed description of the image as a textual representation. It also advises to pay attention to captions or the section the picture appears in.</li>
<li>The last paragraph suggests to ignore certain elements of the page or document, like navigation elements, and also ask the LLM to create chunks of text for the whole document without missing anything, and to quote the text as is, without any rewriting or summarization.</li>
</ul>
<p>Generally, the LLM will generate a number of questions that makes sense for the length and semantic density of the chunk. A smaller paragraph with more pieces of key information could yield more questions than a much longer paragraph that doesn’t carry much semantic meaning.</p>
<p>It’s important to give further advice on which content it can safely ignore, or that it should really take the text verbatim without any creative changes it could think of.</p>
<p>For the illustrations, not only will the LLM give you a detailed description of them, but it will also generate questions that can be answered by these illustrations. For example, if it’s a picture of a flag of Germany, it may generate a question like what are the colors of the German flag.</p>
<h2 id="defining-the-structured-output">Defining the structured output</h2>
<p>In order to guide the LLM to generate a useful JSON output that you can then later parse easily, you can define its structured output JSON schema. Models like Gemini support this feature. Sometimes, some models require you to define the format of the JSON output via prompting, but more modern and bigger models usually support this feature out of the box.</p>
<p>The structured output I defined:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#4070a0">&#34;array&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;items&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#4070a0">&#34;object&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;properties&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;title&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#4070a0">&#34;string&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;description&#34;</span>: <span style="color:#4070a0">&#34;The title of the content.&#34;</span>
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;text&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#4070a0">&#34;string&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;description&#34;</span>: <span style="color:#4070a0">&#34;The text content.&#34;</span>
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;page&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#4070a0">&#34;integer&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;description&#34;</span>: <span style="color:#4070a0">&#34;The page number.&#34;</span>
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;questions&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#4070a0">&#34;array&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;description&#34;</span>: <span style="color:#4070a0">&#34;A list of questions whose answers can be found in this pience of text.&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;items&#34;</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#4070a0">&#34;string&#34;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;required&#34;</span>: [<span style="color:#4070a0">&#34;title&#34;</span>, <span style="color:#4070a0">&#34;text&#34;</span>, <span style="color:#4070a0">&#34;page&#34;</span>, <span style="color:#4070a0">&#34;questions&#34;</span>]
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Here, we have an array of pieces that contain a title, a text chunk (which might be the detailed description of a picture), a page, and a list of hypothetical questions. All those fields are required.</p>
<p>I didn’t put any top level information like the title of the whole document, its location (URL/URI or unique ID), or potentially some tags that help categorize the document. But these are extremely useful bits of information you can also request the LLM to generate for you. I wrote about <a href="https://glaforge.dev/posts/2024/08/12/let-llm-suggest-instagram-hashtags/">using LLMs to tag pictures</a> in an older article.</p>
<h1 id="the-prompt-in-action"><strong>The prompt in action</strong></h1>
<p>I’ve exported the Wikipedia article about the city of <a href="https://en.wikipedia.org/wiki/Berlin">Berlin</a>. I saved it as a PDF document.
Then I used the prompt from the previous section as <strong>system instructions</strong>. I passed the PDF document as a user message. I simply tweaked my prompt by using <a href="https://aistudio.google.com/prompts/new_chat">Google AI Studio</a> but feel free to use any tool you prefer.</p>
<p>Let’s see some extracts of the JSON that Gemini generated for this PDF.</p>
<p>For the first chunk, it followed exactly the content of the first paragraph of the Wikipedia page:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;page&#34;</span>: <span style="color:#40a070">1</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;questions&#34;</span>: [
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;What is Berlin&#39;s status in Germany?&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;What is the population of Berlin?&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;Which state surrounds Berlin?&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;What is the population of Berlin&#39;s urban and metropolitan areas?&#34;</span>
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;text&#34;</span>: <span style="color:#4070a0">&#34;Berlin (/bɜːrˈlɪn/ bur-LIN; German: [bɛɐ̯ˈliːn]) is the capital and largest city of Germany, by both area and population.[10] With 3.7 million inhabitants,[5] it has the highest population within its city limits of any city in the European Union. The city is also one of the states of Germany, being the third smallest state in the country by area. Berlin is surrounded by the state of Brandenburg, and Brandenburg&#39;s capital Potsdam is nearby. The urban area of Berlin has a population of over 4.6 million and is therefore the most populous urban area in Germany.[6][11] The Berlin-Brandenburg capital region has around 6.2 million inhabitants and is Germany&#39;s second-largest metropolitan region after the Rhine-Ruhr region,[5] as well as the fifth-biggest metropolitan region by GDP in the European Union.[12]&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;title&#34;</span>: <span style="color:#4070a0">&#34;Berlin&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>It used <code>Berlin</code> as title, <code>1</code> as we’re on the first page, and it generated 4 questions, including questions related to the information about the population of the city, where it’s geographically situated, or what its relationship with the country itself (it’s the capital).</p>
<p>For the info box showing the flag and coat of arms of the city:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span> {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;page&#34;</span>: <span style="color:#40a070">1</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;questions&#34;</span>: [
</span></span><span style="display:flex;"><span>      <span style="color:#4070a0">&#34;What does the flag of Berlin look like?&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#4070a0">&#34;What does the coat of arms of Berlin depict?&#34;</span>
</span></span><span style="display:flex;"><span>    ],
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;text&#34;</span>: <span style="color:#4070a0">&#34;The infobox shows the flag of Berlin, which is a horizontal tricolor of red, white, and red, with the city&#39;s coat of arms in the center of the white band. The coat of arms of Berlin is also shown, depicting a black bear standing rampant with its tongue out on a silver shield, topped by a golden crown.&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;title&#34;</span>: <span style="color:#4070a0">&#34;Flag and Coat of arms&#34;</span>
</span></span><span style="display:flex;"><span>  }<span style="">,</span>
</span></span></code></pre></div><p>It described clearly the orientation and colors of the bands of the German flag, and described the bear on the coat of arms. The title also mentioned the flag and coat of arms. Some embedding models sometimes accept some title metadata that help them better understand the context for calculating vector embeddings. The questions are also quite obvious: what the flag looks like or what is depicted in the coat of arms.</p>
<p>I won’t go through the whole (and long!) document, but you’ll notice that for some short paragraphs with dense information, the LLM can generate a lot more questions than for a paragraph that doesn’t say much (for example with more descriptive but less important details).</p>
<h1 id="what-to-do-with-this-structured-output"><strong>What to do with this structured output?</strong></h1>
<p>As we’ve activated structured output, we had the following JSON structure returned by the LLM:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>[
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>     <span style="color:#062873;font-weight:bold">&#34;page&#34;</span>: <span style="color:#40a070">1</span>,
</span></span><span style="display:flex;"><span>     <span style="color:#062873;font-weight:bold">&#34;questions&#34;</span>: [
</span></span><span style="display:flex;"><span>       <span style="color:#4070a0">&#34;question 1&#34;</span>, <span style="color:#4070a0">&#34;question 2&#34;</span><span style="">...</span>
</span></span><span style="display:flex;"><span>     ],
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;text&#34;</span>: <span style="color:#4070a0">&#34;...&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;title&#34;</span>: <span style="color:#4070a0">&#34;...&#34;</span>
</span></span><span style="display:flex;"><span>  }<span style="">...</span>
</span></span><span style="display:flex;"><span>]
</span></span></code></pre></div><p>With that JSON document, you can go through each chunk, calculate vector embeddings for both questions and the chunk itself, and store the page and title as metadata, in a database supporting vector operations. Then you’re ready to go with your RAG pipeline!</p>
<h1 id="implementation-with-langchain4j"><strong>Implementation with LangChain4j</strong></h1>
<p>This approach is pretty straightforward to implement with any framework, and of course, with <a href="https://docs.langchain4j.dev/">LangChain4j</a> in Java.</p>

<details>
  <summary>Click to view the code and explanations</summary>
  <p>First, describe the data structure that will be used for the structured output:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Description</span>(<span style="color:#4070a0">&#34;A single piece of content&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">PieceOfContent</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Description</span>(<span style="color:#4070a0">&#34;Number of the page this text appears in&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>page,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Description</span>(<span style="color:#4070a0">&#34;Title of the section of this text&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>title,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Description</span>(<span style="color:#4070a0">&#34;The text chunk&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>text,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Description</span>(<span style="color:#4070a0">&#34;List of questions that can be answered by this text&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>List<span style="color:#666">&lt;</span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>questions<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb"> </span>{}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Then, configure the chat model, here we’ll use Gemini 2.5 Flash, and load the document:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(VertexAiGeminiChatModel<span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-2.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseSchema</span>(SchemaHelper.<span style="color:#4070a0">fromClass</span>(PieceOfContent<span style="color:#666">[]</span>.<span style="color:#4070a0">class</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Path<span style="color:#bbb"> </span>pathToPdf<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Path.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;src/main/resources/Berlin-Wikipedia.pdf&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#902000">byte</span><span style="color:#666">[]</span><span style="color:#bbb"> </span>pdfBytes<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Files.<span style="color:#4070a0">readAllBytes</span>(pathToPdf);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>encodedPdf<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>String(Base64.<span style="color:#4070a0">getEncoder</span>().<span style="color:#4070a0">encode</span>(pdfBytes));<span style="color:#bbb">
</span></span></span></code></pre></div><p>Now, it’s time to prepare the call to the model:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ChatResponse<span style="color:#bbb"> </span>chatResponse<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">chat</span>(ChatRequest.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>.<span style="color:#4070a0">messages</span>(SystemMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your goal is to analyze long documents for Retrieval Augmented
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Generation (RAG) by splitting the content in pieces.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        The content pieces will be the chunks that will be embedded
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        thanks to an embedding model to calculate vector embedding
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        to store in a vector database.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Given a long PDF document, you must extract and format
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        the content so that each piece of content is meaningful
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        as a standalone unit of text.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Each piece of content should be tagged with the page number
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        it appears in, and the title of the section it&#39;s situated in,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        as well as a list of questions whose answers can be provided
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        by this piece of content.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        If there is a table of content, you can use it for adding
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        more context to a given excerpt, or for the section titles,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        but don&#39;t return the table of content as an excerpt.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        When you encounter an image, a picture, or a diagram, the piece
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        of content you return should be the description of the image
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        with as much detail as possible.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        If the figure has a caption, use it as the title of the piece
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        of content.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Like for text excerpts, use the title of the current section
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        the image is in in its description.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Don&#39;t create pieces of text for menu, navigation, references,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        and other unrelated elements.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        But be sure to create a piece of content
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        for all the text of the document.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Go through ALL the pages of the document.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        The piece of text should be the exact text found in the article.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        DON&#39;T change a single world. DON&#39;T summarize.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>TextContent.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;Analyze the following document:&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>PdfFileContent.<span style="color:#4070a0">from</span>(PdfFile.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">base64Data</span>(encodedPdf)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">mimeType</span>(<span style="color:#4070a0">&#34;application/pdf&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">build</span>()))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>.<span style="color:#4070a0">build</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>responseText<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>chatResponse.<span style="color:#4070a0">aiMessage</span>().<span style="color:#4070a0">text</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>PieceOfContent<span style="color:#666">[]</span><span style="color:#bbb"> </span>piecesOfContent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Gson().<span style="color:#4070a0">fromJson</span>(responseText,<span style="color:#bbb"> </span>PieceOfContent<span style="color:#666">[]</span>.<span style="color:#4070a0">class</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>We passed the prompt we talked about, and the PDF file of the Wikipedia page. The model then returns the JSON structure that we unmarshal into an array of our record.</p>
</details>

<h1 id="conclusion"><strong>Conclusion</strong></h1>
<p>Taking advantage of a large language model like Gemini allows you to avoid going through the chunking in your own way, and instead rely on the LLM to do semantic chunking for you. You can read more about Retrieval Augmented Generation in some of my <a href="https://glaforge.dev/tags/retrieval-augmented-generation/">previous articles</a> on the topic.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Advanced RAG — Hypothetical Question Embedding</title><link>https://glaforge.dev/posts/2025/07/06/advanced-rag-hypothetical-question-embedding/</link><pubDate>Sun, 06 Jul 2025 15:57:28 +0200</pubDate><guid>https://glaforge.dev/posts/2025/07/06/advanced-rag-hypothetical-question-embedding/</guid><description>&lt;p>In the first article of this Advanced RAG series, I talked about an approach I called
&lt;a href="https://glaforge.dev/posts/2025/02/25/advanced-rag-sentence-window-retrieval/">sentence window retrieval&lt;/a>,
where we calculate vector embeddings per sentence, but the chunk of text returned
(and added in the context of the LLM) actually contains also surrounding sentences
to add more context to that embedded sentence.
This tends to give a better vector similarity than the whole surrounding context.
It is one of the techniques I&amp;rsquo;m covering in my talk on &lt;a href="https://glaforge.dev/talks/2024/10/14/advanced-rag-techniques/">advanced RAG techniques&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>In the first article of this Advanced RAG series, I talked about an approach I called
<a href="https://glaforge.dev/posts/2025/02/25/advanced-rag-sentence-window-retrieval/">sentence window retrieval</a>,
where we calculate vector embeddings per sentence, but the chunk of text returned
(and added in the context of the LLM) actually contains also surrounding sentences
to add more context to that embedded sentence.
This tends to give a better vector similarity than the whole surrounding context.
It is one of the techniques I&rsquo;m covering in my talk on <a href="https://glaforge.dev/talks/2024/10/14/advanced-rag-techniques/">advanced RAG techniques</a>.</p>
<p>Today, I&rsquo;d like to cover another technique I often use in <strong>applications which are more Question/Answer focused</strong>,
where users ask questions, to find answers contained in the indexed documents: <strong>Hypothetical Question Embedding</strong>.</p>
<p>This is an approach I first discovered in this article which covers both
<a href="https://pixion.co/blog/rag-strategies-hypothetical-questions-hyde">hypothetical question embedding and hypothetical document embedding</a> (HyDE, which we might cover in another article later on).
Comparing user queries to hypothetical questions is the technique we&rsquo;ll study today.</p>
<h2 id="the-intuition-behind-hypothetical-questions">The intuition behind Hypothetical Questions</h2>
<p>When explaining vector similarity (or distance), we usually say that embedding vectors of user queries are closer to vector embeddings of text chunks that contain the answer to that query.
It&rsquo;s generally true, and that&rsquo;s why simple fixed-size chunking approaches (with overlap) work usually pretty well enough.
However, this naive approach compares questions to text containing potential answers.</p>
<p>&#x1f4a1; Intuitively, wouldn&rsquo;t it be better to <strong>compare user questions to other questions</strong>?
Or to compare an hypothetical answer (even if wrong) to text chunks with the answer?</p>
<p>Let&rsquo;s say you want to embed this chunk of text from the Wikipedia page of Berlin:</p>
<blockquote>
<p>Berlin is the capital and largest city of Germany, by both area and population. With 3.7 million inhabitants, it has the highest population within its city limits of any city in the European Union. The city is also one of the states of Germany, being the third smallest state in the country by area. Berlin is surrounded by the state of Brandenburg, and Brandenburg&rsquo;s capital Potsdam is nearby. The urban area of Berlin has a population of over 4.6 million and is therefore the most populous urban area in Germany. The Berlin-Brandenburg capital region has around 6.2 million inhabitants and is Germany&rsquo;s second-largest metropolitan region after the Rhine-Ruhr region, as well as the fifth-biggest metropolitan region by GDP in the European Union.</p></blockquote>
<p>The idea is to ask an LLM, like <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/2-5-pro?utm_campaign=CDR_0x7a40493f_default_b429992869&amp;utm_medium=external&amp;utm_source=blog">Gemini</a>, to generate questions about this paragraph, with a prompt similar to the following:</p>
<blockquote>
<p>Suggest 10 clear questions whose answer could be given by the user provided text.
Don&rsquo;t use pronouns, be explicit about the subjects and objects of the question.</p></blockquote>
<p>You might want to <strong>change the number of questions generated, depending on the length of the chunk of text</strong>,
or if you know that some documents you embed seem to have a higher or lower density of information.
You can also let the LLM figure out on its own how many questions it could ask.</p>
<p>The second sentence of the prompt is critical to avoid the LLM to generate questions with pronouns, like <code>its population is...</code>.
You want <strong><em>fully-qualified</em> named entities</strong>, to have the whole context of the information.</p>
<p>For this paragraph, the LLM could generate 10 questions like the following ones:</p>
<blockquote>
<ol>
<li>What city is the capital of Germany?</li>
<li>What is the population of Berlin within the city limits?</li>
<li>Which city in the European Union has the highest population within its city limits?</li>
<li>What is Berlin&rsquo;s rank by area among the states of Germany?</li>
<li>Which German state surrounds the city of Berlin?</li>
<li>What is the population of the urban area of Berlin?</li>
<li>What is the most populous urban area in Germany?</li>
<li>How many inhabitants does the Berlin-Brandenburg capital region have?</li>
<li>What is Germany&rsquo;s second-largest metropolitan region?</li>
<li>What is the rank of the Berlin-Brandenburg metropolitan region by GDP in the European Union?</li>
</ol></blockquote>
<p>When comparing the user query <code>What is the population of Berlin</code> or <code>How many inhabitants live in Berlin?</code>,
it would match better (higher similarity) with the second generated question: <code>What is the population of Berlin within the city limits?</code>.</p>
<p><strong>When storing the results in the database with vector support, you will have one record per question.</strong>
You will have the vector embedding of each question, associated with the whole paragraph each time.
There&rsquo;s redundancy here, as the chunk of text is repeated as many times as there are questions.
So this is a technique that uses more space.
And it takes also more time (and potentially higher cost) to embed a whole document as you have to call an LLM for each chunk.
But we&rsquo;ll come back to the pros and cons in the following section.</p>
<p><strong>Upon retrieval, the user question is compared to all those generated questions.</strong>
<strong>And at prompt augmentation time, it&rsquo;s the text chunk that is returned, not the generated question.</strong></p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition tip">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M272 384c9.6-31.9 29.5-59.1 49.2-86.2c0 0 0 0 0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4c0 0 0 0 0 0c19.8 27.1 39.7 54.4 49.2 86.2l160 0zM192 512c44.2 0 80-35.8 80-80l0-16-160 0 0 16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z"/></svg>
        <span>Try it out</span>
      </div>
      <div class="admonition-content">
        <p>If you want to test this idea of <strong>hypothetical question embeddidng</strong>, feel free to go ahead and try this
<a href="https://hypothetical-questions-1029513523185.europe-west1.run.app/">application</a>
I vibe-coded with <a href="https://gemini.google.com/canvas">Gemini Canvas</a> (for scaffolding the UI) and
<a href="https://blog.google/technology/developers/introducing-gemini-cli-open-source-ai-agent/?utm_campaign=CDR_0x7a40493f_default_b429992869&amp;utm_medium=external&amp;utm_source=blog">Gemini CLI</a>
(for creating the server app) and deployed to Cloud Run.</p>
<p>You can:</p>
<ul>
<li>enter a chunk of text,</li>
<li>generate hypothetical questions for that chunk,</li>
<li>compare the vector embeddings between the user query, the document, and the hypothetical questions.</li>
</ul>
<p><a href="https://hypothetical-questions-1029513523185.europe-west1.run.app/"><figure>
  <a href="#img-4a7921b283e3307491132080d3fe3aaa">
    <img src="/img/rag/hypothetical-questions-ui.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-4a7921b283e3307491132080d3fe3aaa">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/rag/hypothetical-questions-ui.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</a></p>
      </div>
    </div><h2 id="hypothetical-question-embedding-vs-fixed-sized-chunk-embedding">Hypothetical Question embedding vs fixed-sized chunk embedding</h2>
<p>What are the pros and cons of each approach?</p>
<h3 id="classical-fixed-sized-chunk-embedding">Classical Fixed-Sized Chunk Embedding</h3>
<p>This is the most straightforward method. You simply split your documents into chunks of a fixed size (e.g., 500 characters) and then create an embedding for each chunk.</p>
<p><strong>Pros: 👍</strong></p>
<ul>
<li><strong>Simplicity and speed</strong>: It&rsquo;s easy to implement and computationally efficient, making it great for large datasets.</li>
<li><strong>Predictable size</strong>: Uniform chunk sizes make it easy to manage and process without resource spikes.</li>
</ul>
<p><strong>Cons: 👎</strong></p>
<ul>
<li><strong>Context splitting</strong>: This method can cut sentences or even words in half, leading to a loss of meaning and context. However this is generally mitigated thanks to using an overlap between chunks.</li>
<li><strong>Loss of coherence</strong>: Arbitrarily splitting text can make it difficult for the model to understand the overall narrative or argument.</li>
<li><strong><em>&ldquo;Lost in the middle&rdquo;</em> problem</strong>: Important information can be lost if it&rsquo;s located in the middle of a long document, as the smaller, more focused chunks might not capture the broader context. It can be mitigated with storing and returning a wider surrounding context than what was embedded (i.e. the technique from the <a href="https://glaforge.dev/posts/2025/02/25/advanced-rag-sentence-window-retrieval/">first article</a> of this series).</li>
</ul>
<h3 id="hypothetical-question-embedding">Hypothetical Question Embedding</h3>
<p>With this technique, you use a language model to generate questions for each chunk of your document. Then, you embed these questions instead of the document chunks themselves. When a user asks a question, the system compares their query to the embedded hypothetical questions. But when augmenting the context of the LLM with the results of the vector search, you actually return the inital text from which questions were extracted.</p>
<p><strong>Pros: 👍</strong></p>
<ul>
<li><strong>Improved alignment</strong>: You are comparing a question to a question, which can lead to better semantic matching than comparing a question to a document chunk. This can significantly improve retrieval accuracy.</li>
<li><strong>Addresses the <em>&ldquo;Lost in the Middle&rdquo;</em> problem</strong>: By generating questions for all parts of a document, you&rsquo;re more likely to retrieve relevant information regardless of where it is.</li>
</ul>
<p><strong>Cons: 👎</strong></p>
<ul>
<li><strong>Increased index size</strong>: Generating multiple questions per document chunk means your vector index can become much larger, potentially slowing down search and increasing storage costs. This is because you store the chunk of text as many times as the number of questions which were generated.</li>
<li><strong>Upfront computational cost</strong>: You need to use a language model to generate all the hypothetical questions, which can be time-consuming and expensive if you use hosted models that you pay by the token.</li>
<li><strong>Quality depends on generated questions</strong>: The effectiveness of this method is entirely dependent on the quality of the generated questions. If the language model fails to generate relevant questions, the retrieval will suffer.</li>
<li><strong>Non-deterministic questions</strong>: When you re-index your documents (for example because there was an update) even when using the same embedding model, as this approach relies on an LLM, the new batch of questions might be quite different, as the LLM won&rsquo;t necessarily generate the same questions each time.</li>
</ul>
<h2 id="implementation-details">Implementation details</h2>
<p>The Hypothetical Question approach can be implemented in any language or framework.
But for the sake of this article, I&rsquo;ll be using <a href="https://docs.langchain4j.dev/">LangChain4j</a> in Java.</p>
<p>You can have a look at the whole <a href="https://github.com/datastaxdevs/conference-2024-devoxx/blob/main/devoxx-rag-naive-to-advanced/src/test/java/devoxx/rag/_3_advanced_rag_ingestion/_37_hypothetical_questions_embedding.java">source code</a> of this implementation, but I&rsquo;ll explain in details below what&rsquo;s happening.</p>
<h3 id="at-ingestion-time">At ingestion time</h3>
<p>Let&rsquo;s ingest the documents, generate questions, and calculate the vector embeddings.</p>

<details>
  <summary>Click to view the code and explanations</summary>
  <p>The first thing to do is to load the document (I saved the Wikipedia article as a text file):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Document<span style="color:#bbb"> </span>documentAboutBerlin<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>FileSystemDocumentLoader.<span style="color:#4070a0">loadDocument</span>(<span style="color:#4070a0">&#34;berlin.txt&#34;</span>,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>TextDocumentParser());<span style="color:#bbb">
</span></span></span></code></pre></div><p>Let&rsquo;s configure the large language model, here with Gemini from Vertex AI,
using a response schema, to force the model to return a JSON array of hypothetical question strings:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>VertexAiGeminiChatModel<span style="color:#bbb"> </span>gemini<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(MODEL_GEMINI_FLASH)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">maxRetries</span>(5)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseSchema</span>(Schema.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setType</span>(Type.<span style="color:#4070a0">ARRAY</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setItems</span>(Schema.<span style="color:#4070a0">newBuilder</span>().<span style="color:#4070a0">setType</span>(Type.<span style="color:#4070a0">STRING</span>).<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>The <code>QuestionParagraph</code> record will hold a pair of question and the current text paragraph whose content can answer the question:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">QuestionParagraph</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>question,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>TextSegment<span style="color:#bbb"> </span>paragraph<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb"> </span>{}<span style="color:#bbb">
</span></span></span></code></pre></div><p>The question/paragraph pairs are held in a list:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>List<span style="color:#666">&lt;</span>QuestionParagraph<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>allQuestionParagraphs<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ArrayList<span style="color:#666">&lt;&gt;</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>I&rsquo;m splitting the document into paragraphs, with paragraphs no longer than 2000 characters, and with an overlap of 100 characters, when the threshold is hit:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>DocumentByParagraphSplitter<span style="color:#bbb"> </span>splitter<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>DocumentByParagraphSplitter(2000,<span style="color:#bbb"> </span>100);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>List<span style="color:#666">&lt;</span>TextSegment<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>paragraphs<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>splitter.<span style="color:#4070a0">split</span>(documentAboutBerlin);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Now comes the interesting part!
For each paragraph, I ask the LLM to generate 10 questions, and I store the question/paragraphs pairs in the <code>allQuestionParagraphs</code> list:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(TextSegment<span style="color:#bbb"> </span>paragraphSegment<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>paragraphs)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>ChatResponse<span style="color:#bbb"> </span>aiResult<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>gemini.<span style="color:#4070a0">chat</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>SystemMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Suggest 10 clear questions whose answer could be given
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            by the user provided text.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Don&#39;t use pronouns, be explicit about the subjects
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            and objects of the question.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;&#34;&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>UserMessage.<span style="color:#4070a0">from</span>(paragraphSegment.<span style="color:#4070a0">text</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#666">[]</span><span style="color:#bbb"> </span>questions<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>gson.<span style="color:#4070a0">fromJson</span>(aiResult.<span style="color:#4070a0">aiMessage</span>().<span style="color:#4070a0">text</span>(),<span style="color:#bbb"> </span>String<span style="color:#666">[]</span>.<span style="color:#4070a0">class</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(<span style="color:#902000">int</span><span style="color:#bbb"> </span>i<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>0;<span style="color:#bbb"> </span>i<span style="color:#bbb"> </span><span style="color:#666">&lt;</span><span style="color:#bbb"> </span>questions.<span style="color:#4070a0">length</span>;<span style="color:#bbb"> </span>i<span style="color:#666">++</span>)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>question<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>questions<span style="color:#666">[</span>i<span style="color:#666">]</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>allQuestionParagraphs.<span style="color:#4070a0">add</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>QuestionParagraph(question,<span style="color:#bbb"> </span>paragraphSegment));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>The other important piece is to calculate the vector embeddings of the questions,
but save the text of the paragraph in the embedding vector store:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>List<span style="color:#666">&lt;</span>TextSegment<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>embeddedSegments<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>allQuestionParagraphs.<span style="color:#4070a0">stream</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">map</span>(questionParagraph<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>TextSegment.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>questionParagraph.<span style="color:#4070a0">question</span>(),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Metadata().<span style="color:#4070a0">put</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>PARAGRAPH_KEY,<span style="color:#bbb"> </span>questionParagraph.<span style="color:#4070a0">paragraph</span>().<span style="color:#4070a0">text</span>())))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">toList</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>List<span style="color:#666">&lt;</span>Embedding<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>embeddings<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>embeddingModel.<span style="color:#4070a0">embedAll</span>(embeddedSegments).<span style="color:#4070a0">content</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>embeddingStore.<span style="color:#4070a0">addAll</span>(embeddings,<span style="color:#bbb"> </span>embeddedSegments);<span style="color:#bbb">
</span></span></span></code></pre></div><p>I&rsquo;m using the <code>text-embedding-004</code> model (configured elsewhere in the code).
You can use also <code>text-embedding-005</code> or the new <code>gemini-embedding-01</code> model,
but the latter lacks batching for now, so you can&rsquo;t embed several text segments at once.</p>
<p>The key aspect to pay attention to is that I store extra metadata: the actual paragraph.
But it&rsquo;s really the hypothetical question whose embedding vector is calculated.
But the metadata will be useful at retrieval time, to inject the paragraph in the LLM prompt.</p>
</details>

<h3 id="at-retrieval-time">At retrieval time</h3>
<p>Now that the ingestion is done, let&rsquo;s have a look at the retrieval phase, when users ask questions.</p>

<details>
  <summary>Click to view the code and explanations</summary>
  <p>Let&rsquo;s use the low-level components of LangChain4j to do the search:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>EmbeddingSearchResult<span style="color:#666">&lt;</span>TextSegment<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>searchResults<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>embeddingStore.<span style="color:#4070a0">search</span>(EmbeddingSearchRequest.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">maxResults</span>(4)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">minScore</span>(0.<span style="color:#4070a0">7</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">queryEmbedding</span>(embeddingModel.<span style="color:#4070a0">embed</span>(queryString).<span style="color:#4070a0">content</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>());<span style="color:#bbb">
</span></span></span></code></pre></div><p>This means we&rsquo;re calculating the vector embedding of <code>queryString</code> (the user&rsquo;s question), and compare it with the other vectors stored in the database. We want to retrieve only 4 results with a minimum similarity score of 0.7 (value ranging between 0 and 1).</p>

    <div class="admonition warning">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>
        <span>Important Remark</span>
      </div>
      <div class="admonition-content">
        <p>A very important remark: be sure to <strong>use the same embedding model for both ingestion and retrieval</strong>.
Otherwise the vector embedding values will likely be drastically different, and will give totally garbage results.</p>
      </div>
    </div><p>Now it&rsquo;s time to do the prompt augmentation, by injecting all the paragraphs associated with the closest vectors of the query:</p>
<p>Concatenate all the relevant paragraphs:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>String<span style="color:#bbb"> </span>concatenatedExtracts<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>searchResults.<span style="color:#4070a0">matches</span>().<span style="color:#4070a0">stream</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">map</span>(match<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>match.<span style="color:#4070a0">embedded</span>().<span style="color:#4070a0">metadata</span>().<span style="color:#4070a0">getString</span>(PARAGRAPH_KEY))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">distinct</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">collect</span>(Collectors.<span style="color:#4070a0">joining</span>(<span style="color:#4070a0">&#34;\n---\n&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;\n---\n&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;\n---\n&#34;</span>));<span style="color:#bbb">
</span></span></span></code></pre></div><p>And augment the prompt with those extracts:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>UserMessage<span style="color:#bbb"> </span>userMessage<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>PromptTemplate.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    You must answer the following question:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    {{question}}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Base your answer on the following documentation extracts:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    {{extracts}}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>).<span style="color:#4070a0">apply</span>(Map.<span style="color:#4070a0">of</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;question&#34;</span>,<span style="color:#bbb"> </span>queryString,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;extracts&#34;</span>,<span style="color:#bbb"> </span>concatenatedExtracts<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)).<span style="color:#4070a0">toUserMessage</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>At the end, it&rsquo;s time to ask the LLM to formulate a response with this augmented prompt:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>chatModel.<span style="color:#4070a0">chat</span>(userMessage).<span style="color:#4070a0">aiMessage</span>().<span style="color:#4070a0">text</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>And voilà!</p>
</details>

<h2 id="fixed-chunk-embedding-or-hypothetical-question-embedding">Fixed chunk embedding or Hypothetical Question Embedding?</h2>
<p>The question I often get is to know which technique should be used: fixed chunking, hypothetical question embedding, or another?
I&rsquo;ll reply with the typical consultant answer: <strong>it depends!</strong></p>
<p>That&rsquo;s the point where I&rsquo;ll tell you that <strong>evaluation is key!</strong>
Hypothetical Question embedding typically work better for applications that are indeed Question/Answer focused.
If users ask questions about their data (let&rsquo;s say, an HR chatbot to ask questions about the vacation policy)
this technique works well.
But maybe for applications where the semantic search is more about finding similar documents, this might not yield the same kind of performance.</p>
<p>It&rsquo;s important to run evaluation on your data, with typical user queries, and check which technique yields better results.
We might cover evaluation in another article, later on.</p>

    <div class="admonition tip">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M272 384c9.6-31.9 29.5-59.1 49.2-86.2c0 0 0 0 0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4c0 0 0 0 0 0c19.8 27.1 39.7 54.4 49.2 86.2l160 0zM192 512c44.2 0 80-35.8 80-80l0-16-160 0 0 16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z"/></svg>
        <span>Learn More</span>
      </div>
      <div class="admonition-content">
        <p>If you want to learn more about evaluation techniques, be sure to check out the <a href="https://atamel.dev/">articles</a>
from my colleague <a href="https://x.com/meteatamel">Mete Atamel</a>.</p>
      </div>
    </div><h2 id="going-forward">Going forward</h2>
<ul>
<li>Play with the <a href="https://hypothetical-questions-1029513523185.europe-west1.run.app/">hypothetical question application</a>
I deployed on <a href="https://cloud.google.com/run?utm_campaign=CDR_0x7a40493f_default_b429992869&amp;utm_medium=external&amp;utm_source=blog">Cloud Run</a> to see the impact on vector similarity.</li>
<li>Have another read of my article on the
<a href="https://glaforge.dev/posts/2025/02/25/advanced-rag-sentence-window-retrieval/">sentence window retrieval</a> technique,
to see if it fits better with the kind of documents you have in your corpus.</li>
<li>You can also watch the <a href="https://glaforge.dev/talks/2024/10/14/advanced-rag-techniques/">talk on advanced RAG techniques</a>,
as well as the slides, to see all the other techniques that can be combined.</li>
<li>But never forget to prepare evaluations on your data, with typical user queries, to compare which techniques yield better results!</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Expanding ADK AI agent capabilities with tools</title><link>https://glaforge.dev/posts/2025/06/15/expanding-ai-agent-capabilities-with-tools/</link><pubDate>Sun, 15 Jun 2025 19:12:27 +0200</pubDate><guid>https://glaforge.dev/posts/2025/06/15/expanding-ai-agent-capabilities-with-tools/</guid><description>&lt;p>In a nutshell, the &lt;strong>AI agent equation&lt;/strong> is the following:&lt;/p>
&lt;blockquote>
&lt;p>&lt;em>&lt;strong>AI Agent = LLM + Memory + Planning + Tool Use&lt;/strong>&lt;/em>&lt;/p>&lt;/blockquote>
&lt;p>AI agents are nothing without tools!
And they are actually more than mere Large Language Model calls.
They require some memory management to handle the context of the interactions (short term, long term, or contextual information like in the
&lt;a href="https://glaforge.dev/tags/retrieval-augmented-generation/">Retrieval Augmented Generation approach&lt;/a>.
Planning is important (with variations around the Chain-of-Thought prompting approach, and LLM with reasoning or thinking capabilities) for an agent to realize its tasks.&lt;/p></description><content:encoded>
<![CDATA[<p>In a nutshell, the <strong>AI agent equation</strong> is the following:</p>
<blockquote>
<p><em><strong>AI Agent = LLM + Memory + Planning + Tool Use</strong></em></p></blockquote>
<p>AI agents are nothing without tools!
And they are actually more than mere Large Language Model calls.
They require some memory management to handle the context of the interactions (short term, long term, or contextual information like in the
<a href="https://glaforge.dev/tags/retrieval-augmented-generation/">Retrieval Augmented Generation approach</a>.
Planning is important (with variations around the Chain-of-Thought prompting approach, and LLM with reasoning or thinking capabilities) for an agent to realize its tasks.</p>
<p>But for agents to be useful and to be able to sense or act upon their environment, the need access to tools.
Generally speaking, <em>tool use</em> is about leveraging LLM&rsquo;s <em>function calling</em> ability, to understand when it needs to request some kind of function to be called to proceed further in its next actions or next steps.</p>
<p>In my previous articles about <a href="https://google.github.io/adk-docs/">ADK</a>, I guided you through the
<a href="https://glaforge.dev/posts/2025/05/20/writing-java-ai-agents-with-adk-for-java-getting-started/">creation of your first AI agent with ADK for Java</a>,
and I even shared a <a href="https://glaforge.dev/posts/2025/05/27/adk-java-github-template/">Github project template</a> to help you get started faster.
But today, I want to explore with you the concept of tools, and what tools are at your disposal when <strong>creating AI agents in Java with ADK</strong>.</p>
<h2 id="built-in-tools">Built-in tools</h2>
<p>ADK comes with a handful of very useful built-in tools:</p>
<ul>
<li>a Google Search tool,</li>
<li>a Python code executor,</li>
<li>an artifact service to store and load files.</li>
</ul>
<h3 id="circumventing-llms-cut-off-date-with-google-search">Circumventing LLM&rsquo;s cut-off date with Google Search</h3>
<p>LLMs&rsquo; knowledge is as recent as the last information of the corpus of training data they were trained on.
For example, if you asked an LLM who won the Roland Garros tennis tournament, since the finals ended just a week ago,
it wouldn&rsquo;t be able to tell you who won the 2025 edition.
However, if you give an LLM access to a search engine, it can answer that question with <em>grounded</em> facts.</p>
<p>To do that with ADK, you need to add the <code>GoogleSearchTool</code> tool:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Given</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>LlmAgent<span style="color:#bbb"> </span>agent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;helpful-assistant&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;a helpful assistant who can search in Google&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You&#39;re a helpful assistant
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        who knows how to search in Google Search.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Today is 2025-06-15.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GoogleSearchTool())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// When</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>List<span style="color:#666">&lt;</span>Event<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>events<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>askAgent(agent,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Who&#39;s the man won Roland Garos 2025?&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Then</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>assertThat(events.<span style="color:#4070a0">get</span>(0).<span style="color:#4070a0">content</span>().<span style="color:#4070a0">get</span>().<span style="color:#4070a0">text</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">containsIgnoringCase</span>(<span style="color:#4070a0">&#34;Alcaraz&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>The Google Search tool is also very useful if you want to build some kind of <em>deep research</em> agent that is able to search the web to collect key information to create complex reports.</p>
<h3 id="executing-code-when-advanced-calculations-or-algorithms-are-needed">Executing code when advanced calculations or algorithms are needed</h3>
<p>LLMs are notoriously bad at math or letter games, and at unrolling complex algorithms needed for reasoning purposes (like logic puzzles).
However, they are pretty good at generating code.
For math or algorithms, they are totally capable of generating the right piece of code that could solve the task at hand.
So if you give an LLM the ability to execute some code it generates, and examine the output of that execution, it&rsquo;s going to be able to understand the problem and give a correct answer.</p>
<p>ADK gives you access to the <code>BuiltInCodeExecutionTool</code> tool:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Given</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>LlmAgent<span style="color:#bbb"> </span>agent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;helpful-assistant&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;a helpful assistant that knows how to code&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You&#39;re a helpful assistant.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Today is 2025-06-10.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>BuiltInCodeExecutionTool())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// When</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>List<span style="color:#666">&lt;</span>Event<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>events<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>askAgent(agent,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;How much is Fibonacci(12) + Hackermann(3,3)?&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Then</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Content<span style="color:#bbb"> </span>content<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>events.<span style="color:#4070a0">get</span>(0).<span style="color:#4070a0">content</span>().<span style="color:#4070a0">get</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>assertThat(content.<span style="color:#4070a0">text</span>()).<span style="color:#4070a0">contains</span>(<span style="color:#4070a0">&#34;205&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>In the example above, Gemini will write some Python code, and execute it inside a sandboxed Python interpreter, to give you the final answer.</p>
<h3 id="tool-to-save-and-load-artifacts">Tool to save and load artifacts</h3>
<p>Last built-in tool I&rsquo;d like to mention briefly: the <code>LoadArtifactsTool</code> tool, to deal with <a href="https://google.github.io/adk-docs/artifacts/">artifacts</a>
(although they&rsquo;d deserve their own article too).</p>
<p>Artifacts are named, versioned text or binary data associated with a user session or associated with a user across sessions.
Such <em>files</em> can be persisted via the artifact service (there&rsquo;s even a Google Cloud Storage artifact service for long term storage).</p>
<p>Artifacts are accessible via methods like <code>saveArtifact()</code>, <code>loadArtifact()</code>, or <code>listArtifacts()</code> on objects like <code>CallbackContext</code> (when adding callbacks to your agents), or <code>ToolContext</code> (when adding tools to your agents in methods taking a <code>ToolContext</code> parameter).
Artifacts can also be accessed via the system instructions used to declare your agent.</p>
<p>I won&rsquo;t dive into details today, but for the sake of completeness, here&rsquo;s how you can configure the tool and mention artifacts in the agent system instructions:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>LlmAgent<span style="color:#bbb"> </span>agent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;helpful-movie-assistant&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        a helpful assistant who knows
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        about some rather unknown or obscure movies
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You&#39;re a helpful movie assistant.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        When asked questions about actors in a movie,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        forget about all intrinsic knowledge, and
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        lookup the details in the artifact {artifact.movies.txt}.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>LoadArtifactsTool())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="custom-tools">Custom tools</h2>
<p>When you need your own piece of logic to help your AI agent, you can create custom tools, via the <code>FunctionTool</code> class.
Custom tools are just regular methods, but with a twist: with some carefully crafted annotations to describe the tool to help LLMs understand what this tool can do.</p>
<p>Let&rsquo;s give the agent access to a <code>moonPhase</code> method inside the <code>ToolsTest</code> class to compute the phase of the moon for a given date:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Given</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>LlmAgent<span style="color:#bbb"> </span>agent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;helpful-assistant&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;a helpful assistant&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You&#39;re a helpful assistant who knows about the moon.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Today is 2025-06-15.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(FunctionTool.<span style="color:#4070a0">create</span>(ToolsTest.<span style="color:#4070a0">class</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;moonPhase&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// When</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>List<span style="color:#666">&lt;</span>Event<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>events<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>askAgent(agent,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;What&#39;s the moon phase today?&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Then</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>text<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>events.<span style="color:#4070a0">get</span>(2).<span style="color:#4070a0">parts</span>().<span style="color:#4070a0">get</span>().<span style="color:#4070a0">get</span>(0).<span style="color:#4070a0">text</span>().<span style="color:#4070a0">get</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>assertThat(text).<span style="color:#4070a0">containsIgnoringCase</span>(<span style="color:#4070a0">&#34;full moon&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>And now let&rsquo;s see what the <code>moonPhase</code> method does (with a hard-coded answer):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Schema</span>(description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;get the moon phase for a given date&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>Map<span style="color:#666">&lt;</span>String,<span style="color:#bbb"> </span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span><span style="color:#06287e">moonPhase</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Schema</span>(name<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;date&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;the date for which to get the moon phase&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>date)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;moon-phase&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;full moon&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>I annotated the <code>moonPhase()</code> method with a <code>@Schema</code> with a description,
as well as the <code>date</code> parameter with both a description and a name.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition important">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>
        <span>Important</span>
      </div>
      <div class="admonition-content">
        <p>This is very important to properly document your custom tool
as LLMs will understand this information and that will help them
figure out how to find the right method to invoke, and which parameters to pass it.</p>
      </div>
    </div><p>As of the time of this writing, the <code>0.1.0</code> release of ADK for Java supports <code>static</code> methods,
but in an upcoming version, it&rsquo;ll be possible to use instance methods as well.</p>
<p>Also note that it is mandatory to return a <code>Map</code>.
The reason is that you either return some kind of complex JSON object (that can be transparently un/marshalled),
or you return a map with some <code>status</code> field in addition to the normal return object,
to help the LLM understand if the execution was successful or not.
For example: <code>{&quot;status&quot;: &quot;success&quot;, &quot;moon-phase&quot;: &quot;full moon&quot;}</code>.</p>
<h3 id="what-about-multimodal-tools">What about multimodal tools?</h3>
<p>Since tool support is done via LLM&rsquo;s function calling capability, it&rsquo;s also limited by it!
Currently, I&rsquo;m not aware of LLMs that are able to generate function calls that contain non-textual information, such as images, videos, etc.
Fortunately, there&rsquo;s a way to circumvent this limitation, thanks to ADK&rsquo;s <code>ToolContext</code>.</p>
<p>Let&rsquo;s say you want to leverage LLM&rsquo;s multimodal ability, by looking at a picture of the moon, and guess the phase of the moon depicted in that image.
Function calling can&rsquo;t pass the image directly, so let&rsquo;s see how you can access the user&rsquo;s full multimodal message via the <code>ToolContext</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Given</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>LlmAgent<span style="color:#bbb"> </span>agent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;helpful-assistant&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;a helpful assistant who can analyze pictures of the moon&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You&#39;re a helpful assistant who knows about the moon.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        When asked a question about the moon, or pictures of the moon,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        you MUST call the `moonPhaseFromImage` function.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(FunctionTool.<span style="color:#4070a0">create</span>(ToolsTest.<span style="color:#4070a0">class</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;moonPhaseFromImage&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>We&rsquo;re still creating a custom tool with <code>FunctionTool.create()</code> as before.
However, our method definition will have an additional parameter: an instance of <code>ToolContext</code>.
Note that it should be named <code>toolContext</code>, otherwise ADK won&rsquo;t be happy.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Schema</span>(description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;get the moon phase by analyzing pictures&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>Map<span style="color:#666">&lt;</span>String,<span style="color:#bbb"> </span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span><span style="color:#06287e">moonPhaseFromImage</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Schema</span>(name<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;toolContext&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>ToolContext<span style="color:#bbb"> </span>toolContext)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Optional<span style="color:#666">&lt;</span>List<span style="color:#666">&lt;</span>Part<span style="color:#666">&gt;&gt;</span><span style="color:#bbb"> </span>optionalParts<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>toolContext.<span style="color:#4070a0">userContent</span>().<span style="color:#4070a0">flatMap</span>(Content::parts);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(optionalParts.<span style="color:#4070a0">isPresent</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>List<span style="color:#666">&lt;</span>Part<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>imageParts<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>optionalParts.<span style="color:#4070a0">get</span>().<span style="color:#4070a0">stream</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">filter</span>(part<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>part.<span style="color:#4070a0">inlineData</span>().<span style="color:#4070a0">isPresent</span>()).<span style="color:#4070a0">toList</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(imageParts.<span style="color:#4070a0">size</span>()<span style="color:#bbb"> </span><span style="color:#666">==</span><span style="color:#bbb"> </span>1)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>Part<span style="color:#bbb"> </span>imagePart<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>imageParts.<span style="color:#4070a0">get</span>(0);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#902000">byte</span><span style="color:#666">[]</span><span style="color:#bbb"> </span>imageBytes<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>imagePart.<span style="color:#4070a0">inlineData</span>().<span style="color:#4070a0">get</span>().<span style="color:#4070a0">data</span>().<span style="color:#4070a0">get</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#60a0b0;font-style:italic">// do something with the image bytes...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#60a0b0;font-style:italic">// make a normal multimodal LLM call</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#60a0b0;font-style:italic">// and return the result</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;moon-phase&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;half moon&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;moon-phase&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;unknown&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>The key line to look at (in addition to the <code>toolContext</code> parameter in the signature of the method) is the <code>toolContext.userContent()</code> call.
It gives you access to the <code>Part</code>s of the <code>Content</code> object which represents the user request.</p>
<p>In this code snippet above, we just retrieve the bytes of the uploaded image, and we&rsquo;re faking doing something with them.
But that&rsquo;s the place where you could make an LLM invocation to ask to analyze the image, and guess the phase of the moon depicted in the image.</p>
<h2 id="long-running-custom-tools">Long running custom tools</h2>
<p>So far, I&rsquo;ve talked about tools that are pretty much synchronous in nature, as they usually answer quite rapidly.
But what about situations where you have <strong>long-running workflows</strong> that take several hours or even days to run?</p>
<p>Or what about scenarios where there&rsquo;s the need for a <strong>human in the loop</strong> to validate some action, like a manager who needs to accept or reject an expense report from an employee? That&rsquo;s where <strong>long-running custom tools</strong> come in handy.</p>
<p>ADK offers <code>LongRunningFunctionTool</code>s.
In terms of API, they are exactly like <code>FunctionTool</code>s.
It&rsquo;s just that the framework knows the function will acknowledge the reception of the request, but the full completion of the request may happen at a later time.</p>
<p>Something that confused me initially was that working with LLMs is very <em>request / response</em> oriented,
in the sense that there&rsquo;s always an input from a user, that leads to an output from the LLM.
And the conversation goes on and so forth.</p>
<p>What bothered me was the fact that I didn&rsquo;t know what would happen when we actually receive the final completion answer from the LLM.
Or even how will we receive that completion status?
Well, the thing is that we need to shift our mindset from the request / response turns approach,
and instead think of the fact that ADK is more like an <em>event loop</em>, which doesn&rsquo;t need a response to always follow a request,
but events can flow in and out, from the user, or from the system itself, in any order.</p>
<p>Let&rsquo;s configure a long running function:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Given</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>LlmAgent<span style="color:#bbb"> </span>agent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;helpful-assistant&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;a helpful assistant who can execute workflows&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;You&#39;re a helpful assistant.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(LongRunningFunctionTool.<span style="color:#4070a0">create</span>(ToolsTest.<span style="color:#4070a0">class</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                          </span><span style="color:#4070a0">&#34;executeWorkflow&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>This looks just like a normal <code>FunctionTool</code> declaration.</p>
<p>Now let&rsquo;s send a&hellip; <em>recipe</em> workflows!</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// When</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>List<span style="color:#666">&lt;</span>Event<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>events<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>askAgent(agent,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Execute the following workflow:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    - peel the potatoes
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    - cut the potatoes in dice
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    - put olive oil in the pan
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    - heat pan
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    - put the potato dices
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    - stir regularly
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    - cook for 10 minutes
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Baking some potatoes can take some time, so you&rsquo;re not going to eat your cooked potatoes immediately!</p>
<p>Let&rsquo;s have a look at what our <code>executeWorkflow</code> method does:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Schema</span>(description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    execute a long running workflow made of
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    several steps explained in natural language
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>Map<span style="color:#666">&lt;</span>String,<span style="color:#bbb"> </span>Object<span style="color:#666">&gt;</span><span style="color:#bbb"> </span><span style="color:#06287e">executeWorkflow</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Schema</span>(name<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;workflowDescription&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;description of the workflow to execute&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>workflowDescription,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Schema</span>(name<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;workflowSteps&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;a list of workflow steps&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>List<span style="color:#666">&lt;</span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>workflowSteps)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;status&#34;</span>,<span style="color:#bbb"> </span>LongRunningOperation.<span style="color:#4070a0">Status</span>.<span style="color:#4070a0">STARTED</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;longRunningOperation&#34;</span>,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>LongRunningOperation(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>LongRunningOperation.<span style="color:#4070a0">Status</span>.<span style="color:#4070a0">STARTED</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>workflowDescription,<span style="color:#bbb"> </span>workflowSteps));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>My <code>executeWorkflow</code> method actually returns immediately, to acknowledge the reception of the request.
We can imagine here that we&rsquo;re sending the steps (here a list of strings) to some workflow execution engine.
As return type of the method, I&rsquo;ve decided to return a <code>status</code> indicating that the long running operation has started.
And I also return (in the same map) some <code>LongRunningOperation</code> object with the status, the workflow description, and the steps.</p>
<p>I&rsquo;ve defined this <code>LongRunningOperation</code> as a record, and the status itself as a <code>Status</code> <code>enum</code>.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">LongRunningOperation</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>LongRunningOperation.<span style="color:#4070a0">Status</span><span style="color:#bbb"> </span>status,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>description,<span style="color:#bbb"> </span>List<span style="color:#666">&lt;</span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>steps)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">enum</span><span style="color:#bbb"> </span>Status<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>STARTED,<span style="color:#bbb"> </span>FINISHED,<span style="color:#bbb"> </span>ERROR<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>But you can, of course, return some more complex object that contains an ID to be able to identify the workflow execution.
So that we know which execution completed.</p>
<p>At this point, the LLM used by ADK will reply to the user to acknowledge the reception of the request, and the start of the workflow.
Later on, an event should be sent back via ADK somehow, to notify your application of the completion of the workflow,
in a more event-oriented approach compared to the usual request/response approach of LLM conversations.</p>
<p>The example above is a dummy one, as we&rsquo;re not really executing a long running operation, and we&rsquo;re not going to receive an event upon completion.
So my idea is that I&rsquo;ll come back with an article later on that will dive deeper into long running operations and human in the loop scenarios,
as I believe we need a full article and complete demonstration to illustrate this concept more thoroughly.</p>
<h2 id="an-agent-as-a-tool">An agent as a tool</h2>
<p>So far, we talked about built-in tools, and custom tools, but there&rsquo;s another kind of tool which is quite powerful, and which turn another agent into a tool itself!</p>
<p>Let&rsquo;s come back to our moon phase example.
First, let&rsquo;s define the agent that has access to the <code>moonPhase()</code> function.
It&rsquo;ll be the agent that will serve as a tool:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>LlmAgent<span style="color:#bbb"> </span>moonAgent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;moon-agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;Agent that knows about the moon&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You know everything about the moon!
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Today is 2025-06-15.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        When asked about the phase of the moon,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        call the `moonPhase` tool with the current date as parameter.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>FunctionTool.<span style="color:#4070a0">create</span>(ToolsTest.<span style="color:#4070a0">class</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;moonPhase&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Now let&rsquo;s see how we can turn this sidekick agent into a tool, via the <code>AgentTool.create()</code> method:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>LlmAgent<span style="color:#bbb"> </span>mainAgent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;helpful-assistant&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;a helpful assistant who knows about the moon&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You&#39;re a helpful assistant.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        When a question about the moon is asked,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        ask the question to the `moon-agent` tool.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(AgentTool.<span style="color:#4070a0">create</span>(moonAgent))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// When</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>List<span style="color:#666">&lt;</span>Event<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>events<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>askAgent(mainAgent,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;What&#39;s the moon phase today?&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>I&rsquo;m not showing all the <code>assert</code> statements, but there will be 3 events:
a function execution request addressed to the <code>moon-agent</code>, a function response from the <code>moon-agent</code>, and the final answer by the LLM who will reformulate the function response into a nice human readable answer.</p>
<p>I haven&rsquo;t talked about the various <a href="https://google.github.io/adk-docs/agents/workflow-agents/">agent flows supported by ADK</a>,
like <em>sub-agents</em>, <em>sequential</em> agents, <em>loop</em> agents, and <em>parallel</em> agents, but <strong>agent as tool</strong> is a very powerful pattern for creating more complex agents composed or more specific agents.</p>
<p>Generally, a multi-agent system will be more powerful and more reliable than a big monolithic agent, when tasks can be split and shared among more specialized agents.
I&rsquo;ll come back to this later, in a subsequent article.</p>

    <div class="admonition important">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>
        <span>Important</span>
      </div>
      <div class="admonition-content">
        <p>Today, a limitation of Gemini is that you can&rsquo;t use a function call and a built-in tool at the same time.
ADK, when using Gemini as the underlying LLM, takes advantage of Gemini&rsquo;s built-in ability to do Google searches, and uses function calling to invoke your custom ADK tools.
So agent tools can come in handy, as you can have a main agent, that delegates live searches to a search agent that has the <code>GoogleSearchTool</code> configured,
and another tool agent that makes use of a custom tool function.</p>
<p>Usually, this happens when you get a mysterious error like this <a href="https://github.com/google/adk-python/issues/134">one</a> (reported against ADK for Python):
<code>{'error': {'code': 400, 'message': 'Tool use with function calling is unsupported', 'status': 'INVALID_ARGUMENT'}}</code>.
This means that you can&rsquo;t use a built-in tool and function calling at the same time in the same agent.
The workaround, then, is to decompose the problem into multiple agents, and taking advantage of agent tools.</p>
      </div>
    </div><h2 id="calling-mcp-tools">Calling MCP tools</h2>
<p>Last but not least, let&rsquo;s finish the round of tools with the most trendy one: <strong>MCP</strong> tools (<a href="https://glaforge.dev/tags/model-context-protocol/">Model Context Protocol</a>)!</p>
<p>Last week, I wrote about how to
<a href="https://glaforge.dev/posts/2025/06/09/building-an-mcp-server-with-quarkus-and-deploying-on-google-cloud-run/">create an MCP SSE server with Quarkus</a>,
where I exposed a couple of MCP tools that allow you to know the phase of the moon today or at a custom date — hence the theme again today with the moon.
Let&rsquo;s see how we can configure an agent to use this tool.</p>
<p>It is possible to use Server-Sent Event (SSE for short), or STDIO (standard-in / standard-out) protocols.
The moon phases MCP tool I created the other day was an SSE one.</p>
<p>The first thing to do is to configure it by giving the endpoint of the MCP server:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>SseServerParameters<span style="color:#bbb"> </span>sseParams<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>SseServerParameters.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">url</span>(<span style="color:#4070a0">&#34;https://moonphases-1234567890.europe-west1.run.app/mcp/sse&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Then you can list (and potentially filter manually) the tools available:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>McpToolset.<span style="color:#4070a0">McpToolsAndToolsetResult</span><span style="color:#bbb"> </span>toolsAndToolsetResult<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>McpToolset.<span style="color:#4070a0">fromServer</span>(sseParams).<span style="color:#4070a0">get</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>List<span style="color:#666">&lt;</span>McpTool<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>moonPhasesTools<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>toolsAndToolsetResult.<span style="color:#4070a0">getTools</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>And now you can configure the agent with the list of MCP tools you want to give it access:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>LlmAgent<span style="color:#bbb"> </span>agent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;helpful-assistant&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;a helpful assistant who knows about the moon&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You&#39;re a helpful assistant.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(moonPhasesTools)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>And that&rsquo;s it! Pretty easy, right?</p>
<h2 id="summary">Summary</h2>
<p>In this article, we explored the concept of tools in AI agents, specifically in the context of ADK for Java.
First, we looked at the <strong>built-in tools</strong>, like Google Search, Python code execution, or the artifacts service.
Next, we explored <strong>custom tools</strong>, including <strong>long-running tools</strong>, or also how to <strong>handle multimodal requests</strong> thanks to the tool context.
We discovered the <strong>agent as tool</strong> concept, as a smart agent can be a tool itself for another agent.
Lastly, we also learned about remote <strong>MCP server tools</strong>.</p>
<p>In upcoming articles in this series, we&rsquo;ll dive deeper into some of those tools, and we&rsquo;ll build more complex use cases.
So stay tuned for the next episode!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Building an MCP server with Quarkus and deploying on Google Cloud Run</title><link>https://glaforge.dev/posts/2025/06/09/building-an-mcp-server-with-quarkus-and-deploying-on-google-cloud-run/</link><pubDate>Mon, 09 Jun 2025 13:01:03 +0200</pubDate><guid>https://glaforge.dev/posts/2025/06/09/building-an-mcp-server-with-quarkus-and-deploying-on-google-cloud-run/</guid><description>&lt;p>As I’m contributing to &lt;a href="https://github.com/google/adk-java">ADK&lt;/a> (Agent Development Kit) for Java, and &lt;a href="https://docs.langchain4j.dev/">LangChain4j&lt;/a> (the LLM orchestration framework) I interact with &lt;a href="https://modelcontextprotocol.io/introduction">MCP&lt;/a> (Model Context Protocol) servers and tools to further expand the capabilities of my LLMs.&lt;/p>
&lt;p>Recently, I showed how to &lt;a href="https://glaforge.dev/posts/2025/05/02/vibe-coding-an-mcp-server-with-micronaut-and-gemini/">vibe-code an MCP server using Micronaut&lt;/a>. You know I usually talk about &lt;a href="https://micronaut.io/">Micronaut&lt;/a>, but this time, I wanted to experiment with Quarkus, and in particular with its built-in support for &lt;a href="https://docs.quarkiverse.io/quarkus-mcp-server/dev/index.html">implementing MCP servers&lt;/a>.&lt;/p>
&lt;h2 id="getting-started-with-quarkus-mcp-support">Getting started with Quarkus’ MCP support&lt;/h2>
&lt;p>I created a brand new Quarkus project from IntelliJ IDEA, with its Quarkus template, and I added a couple key dependencies for JSON marshalling, but even more important, for the MCP support:&lt;/p></description><content:encoded>
<![CDATA[<p>As I’m contributing to <a href="https://github.com/google/adk-java">ADK</a> (Agent Development Kit) for Java, and <a href="https://docs.langchain4j.dev/">LangChain4j</a> (the LLM orchestration framework) I interact with <a href="https://modelcontextprotocol.io/introduction">MCP</a> (Model Context Protocol) servers and tools to further expand the capabilities of my LLMs.</p>
<p>Recently, I showed how to <a href="https://glaforge.dev/posts/2025/05/02/vibe-coding-an-mcp-server-with-micronaut-and-gemini/">vibe-code an MCP server using Micronaut</a>. You know I usually talk about <a href="https://micronaut.io/">Micronaut</a>, but this time, I wanted to experiment with Quarkus, and in particular with its built-in support for <a href="https://docs.quarkiverse.io/quarkus-mcp-server/dev/index.html">implementing MCP servers</a>.</p>
<h2 id="getting-started-with-quarkus-mcp-support">Getting started with Quarkus’ MCP support</h2>
<p>I created a brand new Quarkus project from IntelliJ IDEA, with its Quarkus template, and I added a couple key dependencies for JSON marshalling, but even more important, for the MCP support:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>io.quarkus<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>quarkus-resteasy-jackson<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>io.quarkiverse.mcp<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>quarkus-mcp-server-sse<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>1.2.0<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div><p>As I’m going to deploy the server in the cloud, I chose to go with an SSE server: <a href="https://modelcontextprotocol.io/docs/concepts/transports#server-sent-events-sse">Server Sent Events</a>. The STDIO protocol is usually used for MCP servers running locally along the MCP host (i.e. your application invoking the tool).</p>
<p>Instead of going with the usual <em>weather forecast</em> use case, which is a bit like the <em>hello world</em> of MCP servers, I decided to implement a service that calculates the phases of the moon! I got the idea from a recent post on Hackernews that pointed at a GitHub repository that offered <a href="https://github.com/oliverkwebb/moonphase/tree/main">different implementations of the calculation of the moon phases</a>. I used Gemini to convert the algorithm to Java, as there was no Java implementation.</p>
<p>I’ll spare you the details of the calculation, but you can have a look at the <a href="https://github.com/glaforge/moon-phases-quarkus-mcp-sse-server">code</a> I wrote (or Gemini wrote!) to do the math. However, I’ll show you the structure of my <code>MoonPhasesService</code> class:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">jakarta.inject.Singleton</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#555;font-weight:bold">@Singleton</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">MoonPhasesService</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>MoonPhase<span style="color:#bbb"> </span><span style="color:#06287e">currentMoonPhase</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>moonPhaseAtUnixTimestamp(System.<span style="color:#4070a0">currentTimeMillis</span>()<span style="color:#bbb"> </span><span style="color:#666">/</span><span style="color:#bbb"> </span>1000L);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>MoonPhase<span style="color:#bbb"> </span><span style="color:#06287e">moonPhaseAtUnixTimestamp</span>(<span style="color:#902000">long</span><span style="color:#bbb"> </span>timeSeconds)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>This service is able to give you the phase of the moon at this current moment in time, or you can specify a particular date, as a UNIX epoch time in seconds.</p>
<p>This service returns a <code>MoonPhase</code> object. It’s an <code>enum</code> that looks like so:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.fasterxml.jackson.annotation.JsonFormat</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#555;font-weight:bold">@JsonFormat</span>(shape<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>JsonFormat.<span style="color:#4070a0">Shape</span>.<span style="color:#4070a0">OBJECT</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">enum</span><span style="color:#bbb"> </span>MoonPhase<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>NEW_MOON(<span style="color:#bbb">            </span><span style="color:#4070a0">&#34;🌑&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;new moon&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>WAXING_CRESCENT(<span style="color:#bbb">     </span><span style="color:#4070a0">&#34;🌒&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;waxing crescent&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>FIRST_QUARTER(<span style="color:#bbb">       </span><span style="color:#4070a0">&#34;🌓&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;first quarter&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>WAXING_GIBBOUS(<span style="color:#bbb">      </span><span style="color:#4070a0">&#34;🌔&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;waxing gibbous&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>FULL(<span style="color:#bbb">                </span><span style="color:#4070a0">&#34;🌕&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;full&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>WANING_GIBBOUS(<span style="color:#bbb">      </span><span style="color:#4070a0">&#34;🌖&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;waning gibbous&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>LAST_QUARTER(<span style="color:#bbb">        </span><span style="color:#4070a0">&#34;🌗&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;last quarter&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>WANING_CRESCENT(<span style="color:#bbb">     </span><span style="color:#4070a0">&#34;🌘&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;waning crescent&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>NEW_MOON_APPROACHING(<span style="color:#4070a0">&#34;🌑&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;new moon approaching&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// constructur</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// getter/setter for emoji and phase name...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>As I didn’t want to just return the moon phase name, I customized the serialization so that Jackson returns the <code>enum</code> values as normal objects. So, for example, the full moon will be returned as:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;phase&#34;</span>: <span style="color:#4070a0">&#34;full&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;emoji&#34;</span>: <span style="color:#4070a0">&#34;🌕&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Now comes the interesting part! How to expose an MCP tool that LLMs can access? The <code>@Tool</code> and <code>@ToolArg</code> annotations are your friends!</p>
<p>Let’s implement a new class, in which I inject the <code>MoonPhasesService</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">MoonPhasesMcpServer</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Inject</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>MoonPhasesService<span style="color:#bbb"> </span>moonPhasesService;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Now, let’s create two tool methods: one that gives the current phase of the moon, and the other one that gives the phase at a given date.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Tool</span>(name<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;current-moon-phase&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Provides the current moon phase&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>TextContent<span style="color:#bbb"> </span><span style="color:#06287e">currentMoonPhase</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>TextContent(moonPhasesService.<span style="color:#4070a0">currentMoonPhase</span>().<span style="color:#4070a0">toString</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>The first one gives the current phase, as of the date of today. The name and description of the tool are very important, as they help LLMs figure out what this tool is doing, and understand when it should call this tool. I return a <code>TextContent</code> result. But it’s also possible other kinds of content pieces, like audio or image content, or a resource.</p>
<p>Let’s have a look at the other method, the one that gives the moon phase for a given date:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Tool</span>(name<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;moon-phase-at-date&#34;</span>,<span style="color:#bbb"> </span>description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Provides the moon phase at a certain date &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;(with a format of yyyy-MM-dd)&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>ToolResponse<span style="color:#bbb"> </span><span style="color:#06287e">moonPhaseAtDate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@ToolArg</span>(name<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;localDate&#34;</span>,<span style="color:#bbb"> </span>description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;The date for which the user wants to know the phase &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;of the moon (in yyyy-MM-dd format)&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>localDate)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>LocalDate<span style="color:#bbb"> </span>parsedLocalDate<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LocalDate.<span style="color:#4070a0">parse</span>(localDate);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>MoonPhase<span style="color:#bbb"> </span>moonPhase<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>moonPhasesService.<span style="color:#4070a0">moonPhaseAtUnixTimestamp</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>parsedLocalDate.<span style="color:#4070a0">toEpochDay</span>()<span style="color:#bbb"> </span><span style="color:#666">*</span><span style="color:#bbb"> </span>86400);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>ToolResponse.<span style="color:#4070a0">success</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>TextContent(moonPhase.<span style="color:#4070a0">toString</span>()));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">catch</span><span style="color:#bbb"> </span>(DateTimeException<span style="color:#bbb"> </span>dte)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>ToolResponse.<span style="color:#4070a0">error</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#4070a0">&#34;Not a valid date (yyyy-MM-dd): &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>localDate);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>This time, the method also takes an argument. That’s why I annotated the parameter with a <code>@ToolArg</code> annotation, again with a name and description (including how the date should be formatted). Since this method can fail at the time of parsing the date string, I decided to return a <code>ToolResponse</code> which wraps either a result (the moon phase) or an error in case the parsing fails.</p>
<p>As you can see, it’s fairly easy to implement tools for an MCP server! You almost just need annotations, and that’s it!</p>
<p>This server isn’t secured in any way, to keep things simple in this article. But if you need to dig deeper and learn more about securing an MCP server, I invite you to read this article by Sergey Beryozkin on <a href="https://quarkus.io/blog/secure-mcp-sse-server/">getting ready for secure MCP with Qurkus MCP server</a>.</p>
<h2 id="running-the-server-and-checking-it-works">Running the server and checking it works</h2>
<p>To run this MCP server, you can simply run Quarkus in dev mode with the following command (if you’re using Maven):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>./mvnw quarkus:dev
</span></span></code></pre></div><p>You can quickly check that the endpoint is alive and running by simply going to your browser, and hitting this URL: http://localhost:8080/mcp/sse. You’ll see an Server Sent Event like this one:</p>
<pre tabindex="0"><code>event: endpoint
data: /mcp/messages/OTRiYzEyNTItNWY1Ni00NWJhLWExZTEtYzE5ZWU1YjdkNWQy
</code></pre><p>But we’re not really testing our two MCP tools.</p>
<p>An approach is to invoke the MCP server with LangChain4j, you can read more in this article about <a href="https://glaforge.dev/posts/2025/04/04/mcp-client-and-server-with-java-mcp-sdk-and-langchain4j/">MCP client and server with the Java MCP SDK and LangChain4j</a> that I wrote earlier. So I won’t repeat myself today. And you can read more about <a href="https://docs.langchain4j.dev/tutorials/mcp">LangChain4j’s MCP support</a> in its documentation.</p>
<p>But here, I wanted to highlight a very convenient tool: the <a href="https://modelcontextprotocol.io/docs/tools/inspector">MCP inspector</a>. It’s a tool provided by the MCP project itself. It’s a Node-based tool that you can install and run locally on your machine, with the following <code>npx</code> command:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>npx @modelcontextprotocol/inspector
</span></span></code></pre></div><p>It provides a UI to interact with an MCP server. Here, my MCP server is already deployed, I connected to it (I selected SSE, gave the URL of my server), requested the list of tools (shown in the middle pane), and invoked the tool that gives the phase of the moon at a given date (panel on the right of the screenshot):</p>
<p><figure>
  <a href="#img-551eaad4766cbd14f9e9ee64907673b7">
    <img src="/img/adk/mcp-inspector-moon-phases.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-551eaad4766cbd14f9e9ee64907673b7">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/adk/mcp-inspector-moon-phases.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I really encourage you to use the MCP inspector to test your MCP servers manually. This is a very handy tool in your toolbelt.</p>
<h2 id="deploying-on-cloud-run">Deploying on Cloud Run</h2>
<p>So far so good, <em>it works on my machine</em>(™). What about deploying the server in the cloud, since we chose to go with an SSE MCP server? My go-to solution to host my apps quickly and efficiently is to containerize them and deploy them on <a href="https://cloud.google.com/run?utm_campaign=CDR_0x7a40493f_user-journey_b423600838&amp;utm_medium=external&amp;utm_source=blog">Google Cloud Run</a>. Cloud Run is a managed platform to run containers that scale up upon traffic, and down to zero instances when there’s no activity (costing you 0 cent).</p>
<p><a href="https://cloud.google.com/blog/products/ai-machine-learning/ai-studio-to-cloud-run-and-cloud-run-mcp-server?utm_campaign=CDR_0x7a40493f_user-journey_b423600838&amp;utm_medium=external&amp;utm_source=blog">Cloud Run made the highlight at Google I/O</a> this year, as it was announced that you can:</p>
<ul>
<li>Develop apps within <a href="https://aistudio.google.com/">AI Studio</a> and deploy them in one click on Cloud Run,</li>
<li>Deploy a <a href="https://blog.google/technology/developers/gemma-3/?utm_campaign=CDR_0x7a40493f_user-journey_b423600838&amp;utm_medium=external&amp;utm_source=blog">Gemma 3</a> model on Cloud Run again with one click too from <a href="https://aistudio.google.com/prompts/new_chat?model=gemma-3-27b-it">AI Studio</a>,</li>
<li>And run <a href="https://github.com/GoogleCloudPlatform/cloud-run-mcp">Cloud Run’s own MCP server</a> to be able to deploy apps from your MCP powered IDEs and clients.</li>
</ul>
<p>Since Cloud Run is a container based platform, let’s containerize our application. Quarkus offers a handful of <code>Dockerfile</code>s depending on how you want to create your container. For some reason the native build ones didn’t work for me (I got a <em>“the &ndash;chmod option requires BuildKit”</em> error message, that I haven’t investigated further) so I went with the <code>Dockerfile.jvm</code> file, that I copied into <code>Dockerfile</code> at the root of my project, so that Cloud Build could easily pick it up and build it:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>gcloud builds submit <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --tag gcr.io/YOUR_PROJECT_ID/moonphases
</span></span></code></pre></div><p>Once built, it’s available in Google Cloud <a href="https://cloud.google.com/artifact-registry/docs?utm_campaign=CDR_0x7a40493f_user-journey_b423600838&amp;utm_medium=external&amp;utm_source=blog">Artifact Registry</a>. And I can deploy the containerized moon phases service to Cloud Run with the following command:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>gcloud run deploy moonphases <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --allow-unauthenticated <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --image gcr.io/YOUR_PROJECT_ID/moonphases
</span></span></code></pre></div><p>Of course, you’ll have to update the <code>YOUR_PROJECT_ID</code> placeholders with the real Google Cloud project ID of your own project. And along the way, you’ll be requested to enable important APIs (artifact registry, cloud run, etc.)</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>If you’re interested, there’s a great page about <a href="https://cloud.google.com/run/docs/host-mcp-servers?utm_campaign=CDR_0x7a40493f_user-journey_b423600838&amp;utm_medium=external&amp;utm_source=blog">hosting MCP servers on Cloud Run</a>, to learn more about the possibilities.</p>
      </div>
    </div><h2 id="bonus-configuring-the-mcp-server-in-agent-development-kit">Bonus: Configuring the MCP server in Agent Development Kit</h2>
<p>Of course, you can configure and <a href="https://glaforge.dev/posts/2025/04/04/mcp-client-and-server-with-java-mcp-sdk-and-langchain4j/">invoke this MCP server from LangChain4j</a>, but let’s have a quick look at configuring and invoking it from ADK (Agent Development Kit):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>SseServerParameters<span style="color:#bbb"> </span>sseParams<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>SseServerParameters.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">url</span>(<span style="color:#4070a0">&#34;https://moonphases-2029713823481.europe-west1.run.app/mcp/sse&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>McpToolset.<span style="color:#4070a0">McpToolsAndToolsetResult</span><span style="color:#bbb"> </span>toolsAndToolsetResult<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">   </span>McpToolset.<span style="color:#4070a0">fromServer</span>(sseParams).<span style="color:#4070a0">get</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>List<span style="color:#666">&lt;</span>McpTool<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>moonPhasesTools<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>toolsAndToolsetResult.<span style="color:#4070a0">getTools</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>LlmAgent<span style="color:#bbb"> </span>scienceTeacherAgent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;science-app&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;Science teacher agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You&#39;re a friendly science teacher
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        answering questions about scientific concepts.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        If the question is about about the phases of the moon,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        you MUST call the `current-moon-phase` function tool
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        to know the current phase as of right now,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        or the `moon-phase-at-date` function tool
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        to know the phase of the moon on a particular day
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        (the date format is then yyyy-MM-dd).
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(moonPhasesTools)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>I’m not going to detail everything here, but if you want to learn more about ADK for Java, please read my <a href="https://glaforge.dev/posts/2025/05/20/writing-java-ai-agents-with-adk-for-java-getting-started/">getting started guide</a> that I published recently. What’s needed here is to configure the SSE server parameters, creating an MCP toolset, and then getting the list of tools, to pass to the agent via its <code>tools()</code> method.</p>
<p>For the record, here is what the ADK Dev UI shows when asking for the current phase of the moon, and the phase for a later date:</p>
<p><figure>
  <a href="#img-16401cca82a84721e4d25a9288e2eda1">
    <img src="/img/adk/mcp-moon-phases.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-16401cca82a84721e4d25a9288e2eda1">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/adk/mcp-moon-phases.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h2 id="summary">Summary</h2>
<p>In the article, we walked you through building an MCP server with Quarkus and deploying it on Google <a href="https://cloud.google.com/run?utm_campaign=CDR_0x7a40493f_user-journey_b423600838&amp;utm_medium=external&amp;utm_source=blog">Cloud Run</a>.</p>
<p>First, we created a <a href="https://quarkus.io/">Quarkus</a> project with the necessary dependencies for the <a href="https://docs.quarkiverse.io/quarkus-langchain4j/dev/mcp.html">MCP support</a>. Then, we implemented a service to calculate moon phases and exposed it as MCP tools using <code>@Tool</code> and <code>@ToolArg</code> annotations provided by Quarkus. We used the <a href="https://modelcontextprotocol.io/docs/tools/inspector">MCP inspector</a> to test the server and we showed how to configure and invoke it from <a href="https://google.github.io/adk-docs/">ADK</a>, the Agent Development Kit. Finally, we containerized the application and deployed it to Google <a href="https://cloud.google.com/run?utm_campaign=CDR_0x7a40493f_user-journey_b423600838&amp;utm_medium=external&amp;utm_source=blog">Cloud Run</a> for scalability.</p>
<p>If you want to have a closer look at the full source code, you can <a href="https://github.com/glaforge/moon-phases-quarkus-mcp-sse-server">check out this repository</a> to learn more about creating your own MCP servers!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Expanding ADK Java LLM coverage with LangChain4j</title><link>https://glaforge.dev/posts/2025/06/05/expanding-adk-java-llm-coverage-with-langchain4j/</link><pubDate>Thu, 05 Jun 2025 16:41:26 +0200</pubDate><guid>https://glaforge.dev/posts/2025/06/05/expanding-adk-java-llm-coverage-with-langchain4j/</guid><description>&lt;p>Recently on these pages, I&amp;rsquo;ve covered &lt;a href="https://github.com/google/adk-java">ADK&lt;/a> (Agent Development Kit) for Java, launched at Google I/O 2025.
I showed how to get started &lt;a href="https://glaforge.dev/posts/2025/05/20/writing-java-ai-agents-with-adk-for-java-getting-started/">writing your first Java agent&lt;/a>,
and I shared a &lt;a href="https://glaforge.dev/posts/2025/05/27/adk-java-github-template/">Github template&lt;/a> that you can use to kick start your development.&lt;/p>
&lt;p>But you also know that I&amp;rsquo;m a big fan of, and a contributor to the &lt;a href="https://docs.langchain4j.dev/">LangChain4j project&lt;/a>,
where I&amp;rsquo;ve worked on the Gemini support, embedding models, GCS document loaders, Imagen generation, etc.&lt;/p></description><content:encoded>
<![CDATA[<p>Recently on these pages, I&rsquo;ve covered <a href="https://github.com/google/adk-java">ADK</a> (Agent Development Kit) for Java, launched at Google I/O 2025.
I showed how to get started <a href="https://glaforge.dev/posts/2025/05/20/writing-java-ai-agents-with-adk-for-java-getting-started/">writing your first Java agent</a>,
and I shared a <a href="https://glaforge.dev/posts/2025/05/27/adk-java-github-template/">Github template</a> that you can use to kick start your development.</p>
<p>But you also know that I&rsquo;m a big fan of, and a contributor to the <a href="https://docs.langchain4j.dev/">LangChain4j project</a>,
where I&rsquo;ve worked on the Gemini support, embedding models, GCS document loaders, Imagen generation, etc.</p>
<p>How can I reconcile both?
By <strong>integrating ADK and LangChain4j together</strong>!
But why?
Because currently, ADK for Java only supports two models: Gemini and Claude,
compared to the Python version that supports other models via its <a href="https://www.litellm.ai/">LiteLLM</a> integration.
So if I could integrate ADK with LangChain4j, I could make ADK Java access any model that LangChain4j supports!
Then developers could use models from OpenAI, Anthropic, Mistral, and also all the models that can run via Ollama,
like Gemma, Qwen, Phi, and others!</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition warning">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>
        <span>Warning</span>
      </div>
      <div class="admonition-content">
        <p>This is a work-in-progress glimpse into the ADK / LangChain4j integration
I&rsquo;ve been working on with Dmytro (LangChain4j&rsquo;s founder).
It&rsquo;s not yet been integrated in either ADK or LangChain4j.
Currently, it lives as a <a href="https://github.com/google/adk-java/pull/102">Pull Request</a> against the ADK Github project.
Stay tuned! I&rsquo;ll blog back when it&rsquo;s available!</p>
      </div>
    </div><h2 id="using-local-ollama-models-in-adk">Using local Ollama models in ADK</h2>
<p>Let&rsquo;s say you want to build a Java agent with ADK, using the <a href="https://qwenlm.github.io/blog/qwen3/">Qwen 3</a> model,
that you installed locally via Ollama. You have Ollama running on your computer and serving the model.
Then all you have to do is to configure the <a href="https://docs.langchain4j.dev/integrations/language-models/ollama">Ollama LangChain4j</a> model,
and wrap it in a <code>LangChain4j</code> ADK model adapter:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>OllamaChatModel<span style="color:#bbb"> </span>ollamaChatModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>OllamaChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;qwen3:1.7b&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">baseUrl</span>(<span style="color:#4070a0">&#34;http://127.0.0.1:11434&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>LlmAgent<span style="color:#bbb"> </span>scienceTeacherAgent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;science-app&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;Science teacher agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>LangChain4j(ollamaChatModel))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You are a helpful science teacher
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        who explains science concepts to kids and teenagers.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>In the following screenshot of the ADK Dev UI, I configured Ollama to serve a <a href="https://blog.google/technology/developers/gemma-3/">Gemma 3</a> model,
as you can see at the bottom left hand corner, where it shows the events, and LLM requests &amp; responses:</p>
<p><figure>
  <a href="#img-c410473970524fae29802f6dcfb1523b">
    <img src="/img/adk/adk-lc4j-ollama-gemma.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-c410473970524fae29802f6dcfb1523b">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/adk/adk-lc4j-ollama-gemma.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h2 id="using-big-provider-models-in-adk">Using big provider models in ADK</h2>
<p>But you can use the <em>big guns</em> as well, including Anthropic models:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>AnthropicChatModel<span style="color:#bbb"> </span>claudeModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>AnthropicChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;ANTHROPIC_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(CLAUDE_3_7_SONNET_20250219)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>LlmAgent<span style="color:#bbb"> </span>agent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;science-app&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;Science teacher agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>LangChain4j(claudeModel,<span style="color:#bbb"> </span>CLAUDE_3_7_SONNET_20250219))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You are a helpful science teacher
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        who explains science concepts to kids and teenagers.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Or OpenAI, this time using a <strong>streaming model</strong>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>StreamingChatModel<span style="color:#bbb"> </span>openaiStreamingModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>OpenAiStreamingChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;OPENAI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gpt-4o-mini&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>LlmAgent<span style="color:#bbb"> </span>agent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;science-app&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;Science teacher agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>LangChain4j(openaiStreamingModel))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You are a helpful science teacher
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        who explains science concepts to kids and teenagers.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>In the ADK Dev UI, you can flip the switch to enable or disable streaming.
In that case, if you want to support both modes in the UI, <strong>configure two LangChain4j models:
the streaming and the non-streaming one</strong>.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>LlmAgent<span style="color:#bbb"> </span>agent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;science-app&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;Science teacher agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>LangChain4j(openaiModel,<span style="color:#bbb"> </span>openaiStreamingModel))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You are a helpful science teacher
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        who explains science concepts to kids and teenagers.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="what-about-tools">What about tools?</h2>
<p>With ADK, your agents can make use of tools, so if the underlying LangChain4j model supports function calling, tools will work too.
And there&rsquo;s one particular tool that I&rsquo;d like to mention: agent tools.
An agent can be a tool.
So you can mix and match different LLMs as sub-agents, or use a tool backed by a LangChain4j LLM.</p>
<p>For example, here&rsquo;s a main agent using Claude, and a tool agent using OpenAI to give weather information:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>StreamingChatModel<span style="color:#bbb"> </span>openaiStreamingModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>OpenAiStreamingChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;OPENAI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gpt-4o-mini&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>LlmAgent<span style="color:#bbb"> </span>weatherAgent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;weather-agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;Weather agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>LangChain4j(openaiStreamingModel))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to always answer that the weather is sunny and 20°C.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>AnthropicChatModel<span style="color:#bbb"> </span>claudeModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>AnthropicChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;ANTHROPIC_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(CLAUDE_3_7_SONNET_20250219)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>LlmAgent<span style="color:#bbb"> </span>agent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;friendly-weather-app&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;Friend agent that knows about the weather&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>LangChain4j(claudeModel,<span style="color:#bbb"> </span>CLAUDE_3_7_SONNET_20250219))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You are a friendly assistant.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        If asked about the weather forecast for a city,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        you MUST call the `weather-agent` function.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(AgentTool.<span style="color:#4070a0">create</span>(weatherAgent))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Mixing different models in a multi-agent scenario is quite interesting, as you can use the best model for the job.
Maybe you&rsquo;ll need to use a super fast model to do a simple classification task to route requests depending on the ask,
while you&rsquo;ll use a beefier model for the main task that requires more advanced thinking (like a Gemini 2.5 thinking model).</p>
<h2 id="summary">Summary</h2>
<p>This is still early days, as I mentioned in the beginning, it is just a <em>work-in-progress</em> right now,
but I believe it is a great way to <strong>extend ADK Java to supports a lot more models, including local ones</strong>,
and it opens up some <strong>interesting perspectives in multi-agent scenarios when mixing models together</strong>.</p>
<p>Stay tuned, I&rsquo;ll keep you posted on this development!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>An ADK Java GitHub template for your first Java AI agent</title><link>https://glaforge.dev/posts/2025/05/27/adk-java-github-template/</link><pubDate>Tue, 27 May 2025 13:01:52 +0200</pubDate><guid>https://glaforge.dev/posts/2025/05/27/adk-java-github-template/</guid><description>&lt;p>With the unveiling of the &lt;a href="https://github.com/google/adk-java/">Java version&lt;/a> of &lt;a href="https://google.github.io/adk-docs/">Agent Development Kit&lt;/a>
(ADK) which lets you &lt;strong>build AI agents in Java&lt;/strong>, I recently covered how to
&lt;a href="https://glaforge.dev/posts/2025/01/27/an-ai-agent-to-generate-short-scifi-stories/">get started developing your first agent&lt;/a>.&lt;/p>
&lt;p>The installation and quickstart &lt;a href="https://google.github.io/adk-docs/get-started/">documentation&lt;/a> also helps for the first steps,
but I realized that it would be handy to provide a &lt;strong>template project&lt;/strong>, to further accelarate your &lt;em>time-to-first-conversation&lt;/em> with your Java agents!
This led me to play with GitHub&amp;rsquo;s &lt;em>template project&lt;/em> feature, which allows you to create a copy of the template project on your own account or organization.
It comes with a ready-made project structure, a configured &lt;code>pom.xml&lt;/code> file, and a first Java agent you can customize at will, and run from both the command-line or the ADK Dev UI.&lt;/p></description><content:encoded>
<![CDATA[<p>With the unveiling of the <a href="https://github.com/google/adk-java/">Java version</a> of <a href="https://google.github.io/adk-docs/">Agent Development Kit</a>
(ADK) which lets you <strong>build AI agents in Java</strong>, I recently covered how to
<a href="https://glaforge.dev/posts/2025/01/27/an-ai-agent-to-generate-short-scifi-stories/">get started developing your first agent</a>.</p>
<p>The installation and quickstart <a href="https://google.github.io/adk-docs/get-started/">documentation</a> also helps for the first steps,
but I realized that it would be handy to provide a <strong>template project</strong>, to further accelarate your <em>time-to-first-conversation</em> with your Java agents!
This led me to play with GitHub&rsquo;s <em>template project</em> feature, which allows you to create a copy of the template project on your own account or organization.
It comes with a ready-made project structure, a configured <code>pom.xml</code> file, and a first Java agent you can customize at will, and run from both the command-line or the ADK Dev UI.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition tip">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M272 384c9.6-31.9 29.5-59.1 49.2-86.2c0 0 0 0 0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4c0 0 0 0 0 0c19.8 27.1 39.7 54.4 49.2 86.2l160 0zM192 512c44.2 0 80-35.8 80-80l0-16-160 0 0 16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z"/></svg>
        <span>Clone the project</span>
      </div>
      <div class="admonition-content">
        <p>&#x27a1;&#xfe0f; <strong>Clone the <a href="https://github.com/glaforge/adk-java-maven-template"><code>adk-java-maven-template</code></a> project!</strong> &#x2b05;&#xfe0f;</p>
      </div>
    </div><p>The project follows a standard Java project structure:</p>
<pre tabindex="0"><code>project_folder/
├── pom.xml
└── src/
    └── main/
        └── java/
            └── com/
                └── example/
                    └── agent/
                        └── HelloWeatherAgent.java
</code></pre><p>The <code>pom.xml</code> build file declares the two ADK dependencies:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">&lt;!-- The ADK core dependency --&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>com.google.adk<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>google-adk<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>0.1.0<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">&lt;!-- The ADK dev web UI to debug your agent --&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>com.google.adk<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>google-adk-dev<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>0.1.0<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div><p>And the <code>HelloWeatherAgent.java</code> class shows how to create a simple agent:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">//...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;hello-weather-agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;Hello World&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You are a friendly assistant,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        answering questions in a concise manner.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        When asked about weather information,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        you MUST use the `getWeather` function.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(FunctionTool.<span style="color:#4070a0">create</span>(HelloWeatherAgent.<span style="color:#4070a0">class</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;getWeather&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//...</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>An agent that makes use of a tool to request weather forecasts:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">//...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#555;font-weight:bold">@Schema</span>(description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Get the weather forecast for a given city&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>Map<span style="color:#666">&lt;</span>String,<span style="color:#bbb"> </span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span><span style="color:#06287e">getWeather</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@Schema</span>(name<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;city&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>description<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Name of the city to get the weather forecast for&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>city)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;city&#34;</span>,<span style="color:#bbb"> </span>city,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;forecast&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Sunny day, clear blue sky, temperature up to 24°C&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//...</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>There are two ways to run the agent, via the command-line:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mvn compile exec:java <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    -Dexec.mainClass<span style="color:#666">=</span><span style="color:#4070a0">&#34;com.example.agent.HelloWeatherAgent&#34;</span>
</span></span></code></pre></div><p>Or the ADK Dev UI:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mvn compile exec:java <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    -Dexec.mainClass<span style="color:#666">=</span><span style="color:#4070a0">&#34;com.google.adk.web.AdkWebServer&#34;</span> <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    -Dexec.classpathScope<span style="color:#666">=</span><span style="color:#4070a0">&#34;compile&#34;</span>
</span></span></code></pre></div><p>Which will show the nice and handy development UI, to help you prototype and debug your agent:</p>
<p><figure>
  <a href="#img-267fd9b21af432cf80284c9d17e26cd3">
    <img src="https://github.com/glaforge/adk-java-maven-template/raw/main/adk-dev-ui.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-267fd9b21af432cf80284c9d17e26cd3">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://github.com/glaforge/adk-java-maven-template/raw/main/adk-dev-ui.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>&#x1f603; <strong>Happy Java AI agent building!</strong> &#x1f6e0;&#xfe0f;</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Things you never dared to ask about LLMs — Take 2</title><link>https://glaforge.dev/talks/2025/05/26/things-you-never-dared-to-ask-about-llms-take-2/</link><pubDate>Mon, 26 May 2025 12:26:41 +0200</pubDate><guid>https://glaforge.dev/talks/2025/05/26/things-you-never-dared-to-ask-about-llms-take-2/</guid><description>&lt;p>Recently, I had the chance to deliver this talk on the mysteries of LLMs, at &lt;a href="https://www.devoxx.fr/agenda-2025/talk/sous-le-capot-des-llms-toutes-ces-questions-que-vous-n-avez-jamais-ose-poser/">Devoxx France&lt;/a>, with my good friend &lt;a href="https://www.linkedin.com/in/DidierGirard/">Didier Girard&lt;/a>,
It was fun to uncover the oddities of LLMs, and better understand where they thrive or fail, and why.
I also delivered this talk alone at &lt;a href="https://devoxx.pl/talk-details/?id=2171">Devoxx Poland&lt;/a>.&lt;/p>
&lt;p>In this post, I&amp;rsquo;d like to share an update of the presentation deck, with a few additional slides here and there, to cover for example&lt;/p></description><content:encoded>
<![CDATA[<p>Recently, I had the chance to deliver this talk on the mysteries of LLMs, at <a href="https://www.devoxx.fr/agenda-2025/talk/sous-le-capot-des-llms-toutes-ces-questions-que-vous-n-avez-jamais-ose-poser/">Devoxx France</a>, with my good friend <a href="https://www.linkedin.com/in/DidierGirard/">Didier Girard</a>,
It was fun to uncover the oddities of LLMs, and better understand where they thrive or fail, and why.
I also delivered this talk alone at <a href="https://devoxx.pl/talk-details/?id=2171">Devoxx Poland</a>.</p>
<p>In this post, I&rsquo;d like to share an update of the presentation deck, with a few additional slides here and there, to cover for example</p>
<ul>
<li>the difficulty of LLMs to work with acronyms, scientific molecule names, plant names, special uncommon vocabulary, which require more tokens and weakens <em>attention</em>,</li>
<li>the difference between deterministic and probabilistic problems, and why predictive models are still important,</li>
<li>some limits of LLMs with regards to understanding dates, data ownership, or the fact they can&rsquo;t easily forget what they learned.</li>
</ul>
<script async class="speakerdeck-embed" data-id="1d3eae3a34d846888f7183bed5f0597e" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>This was fun delivering the talk with Didier, as a friendly dialogue makes things more entertaining!
We were lucky that this talk was recorded (however, in French &#x1f1eb;&#x1f1f7;) and you can watch the <a href="https://www.youtube.com/watch?v=C6tZE5OgqUc">video</a> below:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/C6tZE5OgqUc?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>Fortunately, this talk was also recorded at Devoxx Poland, in English this time:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/M2FRKPLtG_g?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Beyond the chatbot or AI sparkle: a seamless AI integration</title><link>https://glaforge.dev/posts/2025/05/23/beyond-the-chatbot-or-ai-sparkle-a-seamless-ai-integration/</link><pubDate>Fri, 23 May 2025 14:27:27 +0200</pubDate><guid>https://glaforge.dev/posts/2025/05/23/beyond-the-chatbot-or-ai-sparkle-a-seamless-ai-integration/</guid><description>&lt;p>When I talk about &lt;a href="http://localhost:1313/tags/generative-ai">Generative AI&lt;/a>, whether it&amp;rsquo;s with developers at conferences or with customers, I often find myself saying the same thing: &lt;strong>chatbots are just one way to use Large Language Models&lt;/strong> (LLMs).&lt;/p>
&lt;p>Unfortunately, I see many articles or presentations that just focus on demonstrating LLMs at work within the context of chatbots. I feel guilty of showing the traditional chat interfaces too. But there&amp;rsquo;s so much more to it!&lt;/p></description><content:encoded>
<![CDATA[<p>When I talk about <a href="http://localhost:1313/tags/generative-ai">Generative AI</a>, whether it&rsquo;s with developers at conferences or with customers, I often find myself saying the same thing: <strong>chatbots are just one way to use Large Language Models</strong> (LLMs).</p>
<p>Unfortunately, I see many articles or presentations that just focus on demonstrating LLMs at work within the context of chatbots. I feel guilty of showing the traditional chat interfaces too. But there&rsquo;s so much more to it!</p>
<p>For example, when I <a href="https://glaforge.dev/posts/2025/01/06/analyzing-trends-and-topics-from-blueskys-firehose-with-generative-ai/">analyzed Bluesky topic trends</a>, there was no chatbot involved, but <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/start/quickstarts/quickstart-multimodal?utm_campaign=CDR_0x7a40493f_default_b419777287&amp;utm_medium=external&amp;utm_source=blog">Gemini</a> and <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings?utm_campaign=CDR_0x7a40493f_default_b419777287&amp;utm_medium=external&amp;utm_source=blog">embedding models</a> helped me make sense of clusters of posts. Or when I played with <a href="https://glaforge.dev/posts/2025/01/27/an-ai-agent-to-generate-short-scifi-stories/">generating short science fiction stories</a>, there was again no chat interface, but the LLM and the <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/models/imagen/3-0-generate-002?utm_campaign=CDR_0x7a40493f_default_b419777287&amp;utm_medium=external&amp;utm_source=blog">Imagen</a> image generation models were used for their creative facet.</p>
<p>LLMs are also very powerful to replicate more classical Natural Language Processing tasks (NLP) like sentiment analysis, entity extraction, etc. But sometimes dedicated predictive models are more (cost) effective at those tasks. However, <strong>LLMs allow developers to implement those NLP use cases easily</strong> by properly prompting their favorite model. And developers may then be able to add nice and seamless features here and there in their applications.</p>
<h2 id="the-trigger">The trigger</h2>
<p>What led me to share my views on these usage patterns of AI? It’s when I read Kojo Osei’s article titled “<a href="https://kojo.blog/ai-button/">there should be no AI button</a>”. Indeed, we’re seeing a <strong>proliferation of AI “<em>sparkle”</em> buttons</strong> in various applications and websites. This feels like a quick hack, an extra patch, to say that the application is smart, but it also adds unnecessary cognitive load and breaks the user&rsquo;s focus. <strong>It takes more than an AI sparkle button to make an application intuitive and seamlessly smart</strong>!</p>
<p>The author argues that dedicated &ldquo;AI buttons&rdquo; in user interfaces are a flawed and temporary design choice. The author says, and I agree, that <strong>the best AI user experience is seamless and integrated</strong>, and that AI buttons create unnecessary limitations and frustrations. The article calls for more contextual alternatives that don&rsquo;t artificially segregate AI functionalities, and avoid breaking the flow of the user.</p>
<h2 id="my-take">My take</h2>
<p>In my opinion, the best way to use AI is to build it right into your applications, making them smarter and more helpful <strong>in a way that feels completely natural to the user</strong>. It&rsquo;s not about hiding the fact that AI is involved – on the contrary, I believe users should know. But, as people go about their daily tasks in an app or on a website, AI should be there to assist them smoothly, <strong>without them needing to click a special &ldquo;AI button&rdquo;</strong> to make something happen, or <strong>having to open a chat window to ask for help</strong>.</p>
<p>I see chat interfaces as one specific use case for LLMs, and that&rsquo;s fine. But they aren&rsquo;t the only option, and I don&rsquo;t think they&rsquo;re always the most intuitive or the least disruptive. <strong>People need to stay in their flow</strong>, focused on what they&rsquo;re doing. Their <strong>work should be augmented by AI</strong>, not broken up by extra clicks or messages.</p>
<p>Think about it: if someone is deep in concentration, writing or designing, the last thing they need is to stop, look for a button, and then start a conversation with an AI to get a suggestion. That kind of interruption significantly increases their cognitive load, breaks their focus and makes the whole process feel clunky.</p>
<h2 id="examples-of-more-seamless-and-intuitive-flows">Examples of more seamless and intuitive flows</h2>
<p>Personally, I like when a smart application is proactive but doesn’t get in the way of my normal flow. To illustrate this, let’s think about some common patterns I’ve seen that I found successful at this:</p>
<ul>
<li>
<p>In Gmail and Chat, the UI shows me a <a href="https://support.google.com/chat/answer/12918975?hl=en&amp;co=GENIE.Platform%3DDesktop">summary of the ongoing conversation</a> that I missed. I can still go through the unread messages, but I can also be up-to-speed rapidly by reading the summary, and then quickly glancing through the messages to get more details.</p>
</li>
<li>
<p>Another summarization example is when I use <a href="https://obsidian.md/">Obsidian</a> to take notes of articles I find interesting and want to remember. I installed a Chrome add-on, the <a href="https://help.obsidian.md/web-clipper/interpreter">Obsidian web clipper and its “interpreter”</a>, that I configured to use Gemini, to create a bullet point summary of the articles, and create relevant tags that help me navigate through similar content I’ve already in my notes.</p>
</li>
<li>
<p>For coding, we (developers) are now used to the seamless LLM-powered code completion. I like how it waits a little before suggesting anything. And often, when I use <a href="https://codeassist.google/">Gemini Code Assist</a>, I have the impression that the LLM read my mind and knew exactly about the code I wanted to write. And if it’s not what I wanted, I’m not really disrupted or distracted, as I can continue typing if the suggestion doesn’t make sense.</p>
</li>
<li>
<p>To stay in the realm of developer workflows, your AI peer is at work as the first responder to the tickets users create, then you can hop in the conversation as the user details their issues. Similarly, for PRs (Pull Requests), your AI coding bot can analyze the code you submitted and make first recommendations to improve it, like the <a href="https://github.com/marketplace/gemini-code-assist">Gemini Code Assist bot does on Github</a>.</p>
</li>
<li>
<p>Before creating a ticket, the bug tracker could also take advantage of LLM-powered or embedding-based semantic search to find similar issues, to avoid creating duplicates, or guide the reporter to pick up the right component or category, depending on what the issue is all about.</p>
</li>
<li>
<p>Large Language Models are great at creating first drafts of documents. Give it the right outline, directions, ideas, and you’ll get a first sketch that you can refine — even a few sentences in this very article that you read were first drafted with Gemini!</p>
</li>
<li>
<p>You can also imagine this draft generation in contexts like CRM apps, where users can create a first draft of something they want to send their customers, depending on the current situation of that customer. Or like in the linked article, with this image creation app, where the user draws a few broad strokes of colors to guide the image generation to follow along.</p>
</li>
<li>
<p>But of course if you’re already working in a chat environment (like Slack or similar messaging platforms), it still makes sense to be able to chat with an AI-powered bot! I don’t want to get rid of chat spaces altogether. But an AI assistant should be that: an assistant, a peer, a colleague you can involve where needed, or who can nudge you to tell you <em>“hey, you forgot to add a test”</em>, or <em>“here are the relevant PRDs or bug entries about this new feature you’re talking about”</em>, or <em>“here’s a summary of the relevant past conversations on that topic”</em>.</p>
</li>
</ul>
<p>These are some examples of non-intrusive and seamless integrations, but <strong>UX designers need to rethink the app</strong> or website, and be acquainted with what LLMs can offer. <strong>Making an application smart isn’t just adding an AI sparkle button or a chatbot interface</strong>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>While chatbots are great for things like customer support or answering direct questions, they&rsquo;re not always the best fit for every situation. For many tasks, I believe AI assistance should be <strong>more like a quiet, helpful partner</strong> — there when you need it, maybe even anticipating what you need, without you having to constantly ask.</p>
<p>What I really believe we should aim for is AI that boosts what people are already doing, making their work easier and more powerful without adding extra steps. We should be <strong>creating experiences where AI enhances the tools people use every day</strong>, making them more efficient and insightful, <strong>without forcing them to constantly switch contexts</strong> or explicitly request AI intervention for every little thing via a sparkle button or a chat message.</p>
<p>Ultimately, I think the AI integrations that will truly succeed are the ones that don&rsquo;t feel like an add-on. Instead, they&rsquo;ll feel like a natural, intelligent part of the system. My ideal is to see us build AI experiences that genuinely empower people by <strong>working seamlessly in the background</strong>, helping them <strong>stay focused</strong> and achieve more, more rapidly.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Write AI agents in Java — Agent Development Kit getting started guide</title><link>https://glaforge.dev/posts/2025/05/20/writing-java-ai-agents-with-adk-for-java-getting-started/</link><pubDate>Tue, 20 May 2025 11:48:50 +0200</pubDate><guid>https://glaforge.dev/posts/2025/05/20/writing-java-ai-agents-with-adk-for-java-getting-started/</guid><description>&lt;p>At Google Cloud Next ‘25, last April, Google released &lt;a href="https://google.github.io/adk-docs/">Agent Development Kit&lt;/a> (ADK) for Python, a &lt;strong>flexible and modular framework for developing and deploying AI agents&lt;/strong>.&lt;/p>
&lt;p>Now at Google I/O, a &lt;strong>Java version of ADK&lt;/strong> has been made available! And I’m glad to have had the chance to participate in its launch, via code samples, documentation, and helping shape the API so it’s idiomatic for Java developers.&lt;/p>
&lt;p>In this article, my goal is to give you the basis to get started with the ADK framework, in Java, using the Gemini model, and running your first Java agents locally.&lt;/p></description><content:encoded>
<![CDATA[<p>At Google Cloud Next ‘25, last April, Google released <a href="https://google.github.io/adk-docs/">Agent Development Kit</a> (ADK) for Python, a <strong>flexible and modular framework for developing and deploying AI agents</strong>.</p>
<p>Now at Google I/O, a <strong>Java version of ADK</strong> has been made available! And I’m glad to have had the chance to participate in its launch, via code samples, documentation, and helping shape the API so it’s idiomatic for Java developers.</p>
<p>In this article, my goal is to give you the basis to get started with the ADK framework, in Java, using the Gemini model, and running your first Java agents locally.</p>
<blockquote>
<p>Be sure to checkout this video I recorded on how to get started
and learn more about some key concepts of ADK:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/VM3b3csBeUc?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>And if you want to start from a ready-made project with a sample agent using tools,
checkout the article on my
<a href="https://glaforge.dev/posts/2025/05/27/adk-java-github-template/">GitHub template project</a>,
or a direct link to the
<a href="https://github.com/glaforge/adk-java-maven-template">template project</a>.</p>
<p>In the meantime, let&rsquo;s now focus on the key steps to write your first AI agents with ADK!</p></blockquote>
<h2 id="project-structure">Project structure</h2>
<p>Create a usual Java project structure like the following, with your Java sources in <code>src/main/java</code>:</p>
<pre tabindex="0"><code>[YOUR_PROJECT_DIRECTORY]/
 └—— pom.xml
 └—— src/
     └—— main/
         └—— java/
             └—— agents/
                 └—— ScienceTeacherAgent.java
</code></pre><p>Let’s have a look at a minimal <code>pom.xml</code> to compile and run your agent:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#007020">&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;project</span> <span style="color:#4070a0">xmlns=</span><span style="color:#4070a0">&#34;http://maven.apache.org/POM/4.0.0&#34;</span>
</span></span><span style="display:flex;"><span>         <span style="color:#4070a0">xmlns:xsi=</span><span style="color:#4070a0">&#34;http://www.w3.org/2001/XMLSchema-instance&#34;</span>
</span></span><span style="display:flex;"><span>         <span style="color:#4070a0">xsi:schemaLocation=</span><span style="color:#4070a0">&#34;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;modelVersion&gt;</span>4.0.0<span style="color:#062873;font-weight:bold">&lt;/modelVersion&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>adk-agents<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>adk-agents<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>1.0-SNAPSHOT<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;dependencies&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#60a0b0;font-style:italic">&lt;!-- ADK core dependency --&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>com.google.adk<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>google-adk<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>0.1.0<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#60a0b0;font-style:italic">&lt;!-- ADK dev web UI and API server --&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>com.google.adk<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>google-adk-dev<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>0.1.0<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;/dependencies&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/project&gt;</span>
</span></span></code></pre></div><p>You just need a couple of dependencies:</p>
<ul>
<li>the Core ADK framework dependency, and</li>
<li>the ADK web server that provides a nice and useful Dev UI to interact with your agents, as well as an API server.</li>
</ul>
<h2 id="your-first-agent">Your first agent</h2>
<p>Now that the project is set up, let’s implement a very simple agent. It’ll be a single agent. And it won’t yet use tools to interact with the external world. We’ll explore multi agents and tool support later on, in more advanced articles.</p>
<p>So let’s have a look at a science teacher agent, whose role is to help kids and teenagers understand scientific concepts in a friendly and approachable way.</p>
<p>Here’s the full source code. You can expand this block to see it all, but we’ll explain everything further down, in a piecemeal fashion.</p>

<details>
  <summary>Click to see the full source code</summary>
  <div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">package</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">agents</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import static</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.nio.charset.StandardCharsets.UTF_8</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.Scanner</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.adk.agents.BaseAgent</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.adk.agents.LlmAgent</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.adk.events.Event</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.adk.runner.InMemoryRunner</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.adk.sessions.Session</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.genai.types.Content</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.genai.types.Part</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.reactivex.rxjava3.core.Flowable</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">/** Science teacher agent. */</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">ScienceTeacherAgent</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>BaseAgent<span style="color:#bbb"> </span>ROOT_AGENT<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>initAgent();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>BaseAgent<span style="color:#bbb"> </span><span style="color:#06287e">initAgent</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;science-app&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;Science teacher agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                You are a helpful science teacher that explains
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                science concepts to kids and teenagers.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">main</span>(String<span style="color:#666">[]</span><span style="color:#bbb"> </span>args)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>InMemoryRunner<span style="color:#bbb"> </span>runner<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>InMemoryRunner(ROOT_AGENT);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Session<span style="color:#bbb"> </span>session<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>runner<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">sessionService</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">createSession</span>(runner.<span style="color:#4070a0">appName</span>(),<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;student&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">blockingGet</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(Scanner<span style="color:#bbb"> </span>scanner<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Scanner(System.<span style="color:#4070a0">in</span>,<span style="color:#bbb"> </span>UTF_8))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">while</span><span style="color:#bbb"> </span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">print</span>(<span style="color:#4070a0">&#34;\nYou &gt; &#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>String<span style="color:#bbb"> </span>userInput<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>scanner.<span style="color:#4070a0">nextLine</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(<span style="color:#4070a0">&#34;quit&#34;</span>.<span style="color:#4070a0">equalsIgnoreCase</span>(userInput))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span><span style="color:#007020;font-weight:bold">break</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>Content<span style="color:#bbb"> </span>userMsg<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Content.<span style="color:#4070a0">fromParts</span>(Part.<span style="color:#4070a0">fromText</span>(userInput));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>Flowable<span style="color:#666">&lt;</span>Event<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>events<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>runner.<span style="color:#4070a0">runAsync</span>(session.<span style="color:#4070a0">userId</span>(),<span style="color:#bbb"> </span>session.<span style="color:#4070a0">id</span>(),<span style="color:#bbb"> </span>userMsg);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">print</span>(<span style="color:#4070a0">&#34;\nAgent &gt; &#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>events.<span style="color:#4070a0">blockingForEach</span>(event<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(event.<span style="color:#4070a0">stringifyContent</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>});<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div></details>

<p>So what does the simplest agent look like?</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>LlmAgent.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;science-app&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;Science teacher agent&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">instruction</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You are a helpful science teacher that explains
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        science concepts to kids and teenagers.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>An agent consists of a name, a description, a model, and some instructions, and that’s it! In other articles, we’ll also see how to give it tools, how to use state, how it can interact with other agents and workflows, but for now, let’s stick with this simple science teacher agent.</p>
<h2 id="running-your-agent">Running your agent</h2>
<p>Let’s say you’ve saved the above agent in a <code>ROOT_AGENT</code> static field of your class (it’ll come in handy for the Dev UI later on). Now we need to instantiate a <em>runner</em> and a <em>session</em> to run the agent.</p>
<p>First, you instantiate an <code>InMemoryRunner</code> with your agent:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>InMemoryRunner<span style="color:#bbb"> </span>runner<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>InMemoryRunner(ROOT_AGENT);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Then you create a session with the <code>SessionService</code> that you can grab from the runner itself (passing an application name and user ID):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Session<span style="color:#bbb"> </span>session<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>runner<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">sessionService</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">createSession</span>(runner.<span style="color:#4070a0">appName</span>(),<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;student&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">blockingGet</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Now, if you want to have a familiar chatbot-like experience, you’ll loop over and alternate between user provided messages, and LLM generated answers. Let’s use a <code>Scanner</code> to get the input from the user, inside a big <code>while</code> loop, and send the user’s prompt to the LLM agent, via runner’s <code>runAsync()</code> method, passing the session and the message. The agent will reply with a flow of events that we can then print:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(Scanner<span style="color:#bbb"> </span>scanner<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Scanner(System.<span style="color:#4070a0">in</span>,<span style="color:#bbb"> </span>UTF_8))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">while</span><span style="color:#bbb"> </span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">print</span>(<span style="color:#4070a0">&#34;\nYou &gt; &#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>userInput<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>scanner.<span style="color:#4070a0">nextLine</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(<span style="color:#4070a0">&#34;quit&#34;</span>.<span style="color:#4070a0">equalsIgnoreCase</span>(userInput))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">break</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Content<span style="color:#bbb"> </span>userMsg<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Content.<span style="color:#4070a0">fromParts</span>(Part.<span style="color:#4070a0">fromText</span>(userInput));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Flowable<span style="color:#666">&lt;</span>Event<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>events<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>runner<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">runAsync</span>(session.<span style="color:#4070a0">userId</span>(),<span style="color:#bbb"> </span>session.<span style="color:#4070a0">id</span>(),<span style="color:#bbb"> </span>userMsg);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">print</span>(<span style="color:#4070a0">&#34;\nAgent &gt; &#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>events.<span style="color:#4070a0">blockingForEach</span>(event<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(event.<span style="color:#4070a0">stringifyContent</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>});<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="define-environment-variables">Define environment variables</h2>
<p>You’ll need to export two environment variables:</p>
<ul>
<li>a Gemini key that you can <a href="https://ai.google.dev/gemini-api/docs/api-key">get from AI Studio</a>,</li>
<li>a variable to specify you’re not using Vertex AI this time.</li>
</ul>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#007020">export</span> <span style="color:#bb60d5">GOOGLE_GENAI_USE_VERTEXAI</span><span style="color:#666">=</span>FALSE
</span></span><span style="display:flex;"><span><span style="color:#007020">export</span> <span style="color:#bb60d5">GOOGLE_API_KEY</span><span style="color:#666">=</span>AIzaSyDF...
</span></span></code></pre></div><p>In this article, let’s use the Google AI / DeepMind endpoint and API key for Gemini, but you can also use Gemini from Google Cloud Vertex AI.</p>
<h2 id="executing-the-agent-from-the-command-line">Executing the agent from the command line</h2>
<p>From the command line, let’s use Maven’s exec java plugin to launch this science teacher agent:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mvn compile exec:java -Dexec.mainClass<span style="color:#666">=</span><span style="color:#4070a0">&#34;agents.ScienceTeacherAgent&#34;</span>
</span></span></code></pre></div><p>Let’s see the output when asking a simple question about <em>“qubits”</em>!</p>
<pre tabindex="0"><code>[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------&lt; adk-agents:adk-agents &gt;------------------------
[INFO] Building adk-agents 1.0-SNAPSHOT
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ adk-agents ---
[INFO] skip non existing resourceDirectory /Users/me/Projects/adk-agents/src/main/resources
[INFO]
[INFO] --- compiler:3.13.0:compile (default-compile) @ adk-agents ---
[INFO] Nothing to compile - all classes are up to date.
[INFO]
[INFO] --- exec:3.5.0:java (default-cli) @ adk-agents ---

You &gt; What is a qbit? Please answer in a concise manner.

Agent &gt; Hey there, future tech wiz! 👋

A **qubit** (short for &#34;quantum bit&#34;) is the basic unit of information
in a quantum computer. Unlike a regular bit in your computer, which is
either a 0 or a 1, a qubit can be a 0, a 1, or *both at the same time*
thanks to something called **superposition**! This &#34;both at once&#34;
ability is what makes quantum computers super powerful for certain
kinds of problems.

You &gt; quit
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0:22 min
[INFO] Finished at: 2025-05-19T01:00:12+02:00
[INFO] ------------------------------------------------------------------------
</code></pre><p>And the conversation can go on and on, till you’re bored and you enter <em>“quit”</em> to end the chat.</p>
<h2 id="executing-the-agent-in-the-dev-ui">Executing the agent in the Dev UI</h2>
<p>ADK comes with a Dev web UI (and API server) that you can use to run your agents, see how they behave, the data they exchange, how they interact with the underlying LLM. So let’s talk about this.</p>
<p>The following Maven command will launch the Dev UI:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mvn exec:java <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    -Dexec.mainClass<span style="color:#666">=</span><span style="color:#4070a0">&#34;com.google.adk.web.AdkWebServer&#34;</span> <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    -Dexec.classpathScope<span style="color:#666">=</span><span style="color:#4070a0">&#34;compile&#34;</span>
</span></span></code></pre></div><p><strong>Note</strong>: You can add a different path or even use a sub-directory (if you want to expose only agents in a certain package) by adding and customising this flag: <code>-Dexec.args=&quot;--adk.agents.source-dir=src/main/java/com/foo/agents&quot;</code>.</p>
<p>This time, you run the <code>AdkWebServer</code> class from the second dependency (<code>google-adk-dev</code>). There’s one constraint currently for the Dev UI to grab and run your agents: they have to be statically initialized and stored in a <code>public static BaseAgent ROOT_AGENT</code> field. That’s why the class had the following structure:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">ScienceTeacherAgent</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>BaseAgent<span style="color:#bbb"> </span>ROOT_AGENT<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>initAgent();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>BaseAgent<span style="color:#bbb"> </span><span style="color:#06287e">initAgent</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb"> </span>...<span style="color:#bbb"> </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>To access the Dev UI, just head over to your browser and open the following URL: http://localhost:8080/dev-ui</p>
<p>You should see something similar to this:</p>
<p><figure>
  <a href="#img-b39a6121356dd845d6c6f174fe8b8fe4">
    <img src="/img/adk/adk-dev-ui-science-teacher.png"
      alt="adk-dev-ui-science-teacher.png"
       />
  </a>
  <figcaption>adk-dev-ui-science-teacher.png</figcaption>
</figure>
<div class="lightbox" id="img-b39a6121356dd845d6c6f174fe8b8fe4">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/adk/adk-dev-ui-science-teacher.png"
    alt="adk-dev-ui-science-teacher.png"
     />
  <div class="lightbox-caption">adk-dev-ui-science-teacher.png</div>
</div>
</p>
<ul>
<li>You can <strong>select an agent</strong> from the loaded agents in the top left hand corner.</li>
<li>When clicking on the <strong>events</strong>, you can see the popup over the left pane that shows all the details of the events that are flowing through, as well as the <strong>input request</strong> and <strong>output response</strong> from the LLM.</li>
<li>On the right side, in the chat area, you can see the <strong>dialogue</strong> between the user and the AI, as well as potential <strong>tool calls</strong>.</li>
<li>You can also <strong>enable token streaming</strong> at the top of the screen with the slide if you want to see responses being streamed as they are generated by the model.</li>
</ul>
<h2 id="congratulations">Congratulations!</h2>
<p>Wait, what? Are we already done?
Yes, you’ve coded and executed your first Java agent with the ADK framework!</p>
<h2 id="now-what">Now what?</h2>
<p>Many possible next steps, and also pointers, that I’d like to share with you.</p>
<ul>
<li>First of all, read the <a href="https://developers.googleblog.com/en/agents-adk-agent-engine-a2a-enhancements-google-io/?utm_campaign=CDR_0x7a40493f_default_b418955413&amp;utm_medium=external&amp;utm_source=blog">article announcing the launch of Java ADK</a>, and other news related to <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/agent-engine/overview?utm_campaign=CDR_0x7a40493f_default_b418955413&amp;utm_medium=external&amp;utm_source=blog">Agent Engine</a>, and <a href="https://github.com/google/A2A">A2A</a> (Agent to Agent Protocol).</li>
<li>Think about how you can integrate such an agent inside an existing Java application that you want to enhance with advanced agentic capabilities.</li>
<li>You may want to <a href="https://google.github.io/adk-docs/deploy/cloud-run/">deploy an agent on Cloud Run</a>, for example, or host it anywhere else you’d like, as it’s just an open source framework not tied to <a href="https://cloud.google.com/?utm_campaign=CDR_0x7a40493f_default_b418955413&amp;utm_medium=external&amp;utm_source=blog">Google Cloud</a>.</li>
<li>Start <a href="https://google.github.io/adk-docs/">learning more</a> about the concepts behind the framework, and what it’s capable of.</li>
<li>Check out the <a href="https://github.com/google/adk-java">source code on Github</a>.</li>
<li>I’d like to invite you to look at more substantial <a href="https://github.com/google/adk-docs/tree/main/examples/java/snippets/src/main/java">examples</a> and <a href="https://github.com/google/adk-samples/tree/main/java">samples</a> than my science teacher.</li>
<li>My colleague Abi implemented a <a href="https://medium.com/google-cloud/build-powerful-stateful-ai-agents-in-java-with-agent-development-kit-adk-0f7e2cd3d094">patent research and analysis agent</a> in Java, with ADK, deployed on Cloud Run, with data on AlloyDB.</li>
</ul>
<p>I’m really looking forward to hearing from you about what you’ll be building with ADK for Java! You can expect many more articles on this blog on the topic of ADK. So stay tuned!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Vibe coding an MCP server with Micronaut, LangChain4j, and Gemini</title><link>https://glaforge.dev/posts/2025/05/02/vibe-coding-an-mcp-server-with-micronaut-and-gemini/</link><pubDate>Fri, 02 May 2025 19:35:05 +0200</pubDate><guid>https://glaforge.dev/posts/2025/05/02/vibe-coding-an-mcp-server-with-micronaut-and-gemini/</guid><description>&lt;p>Unlike Quarkus and Spring Boot, Micronaut doesn&amp;rsquo;t (yet?) provide a module to facilitate the implementation of &lt;a href="https://modelcontextprotocol.io/">MCP&lt;/a> servers (Model Context Protocol).
But being my favorite framework, I decided to see what it takes to build a quick implementation, by &lt;em>vibe coding&lt;/em> it, with the help of Gemini!&lt;/p>
&lt;p>In a recent article, I explored &lt;a href="https://glaforge.dev/posts/2025/05/02/vibe-coding-an-mcp-server-with-micronaut-and-gemini/">how to use the MCP reference implementation for Java to implement an MCP server&lt;/a>,
served as a servlet via Jetty, and to call that server from &lt;a href="https://docs.langchain4j.dev/tutorials/mcp/">LangChain4j&amp;rsquo;s great MCP support&lt;/a>.
One approach with Micronaut may have been to somehow integrate the servlet I had built via Micronaut&amp;rsquo;s servlet support, but that didn&amp;rsquo;t really feel like a genuine and native way to implement a server, so I decided to do it from scratch.&lt;/p></description><content:encoded>
<![CDATA[<p>Unlike Quarkus and Spring Boot, Micronaut doesn&rsquo;t (yet?) provide a module to facilitate the implementation of <a href="https://modelcontextprotocol.io/">MCP</a> servers (Model Context Protocol).
But being my favorite framework, I decided to see what it takes to build a quick implementation, by <em>vibe coding</em> it, with the help of Gemini!</p>
<p>In a recent article, I explored <a href="https://glaforge.dev/posts/2025/05/02/vibe-coding-an-mcp-server-with-micronaut-and-gemini/">how to use the MCP reference implementation for Java to implement an MCP server</a>,
served as a servlet via Jetty, and to call that server from <a href="https://docs.langchain4j.dev/tutorials/mcp/">LangChain4j&rsquo;s great MCP support</a>.
One approach with Micronaut may have been to somehow integrate the servlet I had built via Micronaut&rsquo;s servlet support, but that didn&rsquo;t really feel like a genuine and native way to implement a server, so I decided to do it from scratch.</p>
<h2 id="vibe-coding-with-gemini">Vibe coding with Gemini</h2>
<p>The concept of <em>vibe coding</em> came from a <a href="https://x.com/karpathy/status/1886192184808149383">tweet from Andrej Karpathy</a>
who defined the concept as interacting with an LLM to build a new prototype or weekend project, and iterating with the LLM till it works, but without looking at or touching the code yourself.
It&rsquo;s quite a bit different than using AI assistance to build a production-ready code base.
And Simon Willison&rsquo;s just written a good piece on <a href="https://simonwillison.net/2025/May/1/not-vibe-coding/">what is and what is not <em>vibe coding</em></a>.</p>
<p>I started throwing <strong>Gemini 2.5 Pro</strong> some simple prompts for creating an MCP server with Micronaut, with Java 21, but it would not generate something really usable, at least not in one-shot!
For example, it would not use Server-Sent Events, or it hadn&rsquo;t figured out how that the protocol is using JSON-RPC, etc.
So instead of steering the LLM in the right direction via multiple prompts, I reused my tacticts of feeding as much information as needed into the prompt.
This is the approach I took to <a href="https://glaforge.dev/posts/2025/03/03/llms-txt-to-help-llms-grok-your-content/">grok one&rsquo;s own content with LLMs.txt</a>, or when I wrote about
<a href="https://glaforge.dev/posts/2025/02/15/the-power-of-large-context-windows-for-your-documentation-efforts/">the power of large context windows for your documentation efforts</a>.</p>
<p>So what was the successful prompt?</p>
<blockquote>
<p>Let&rsquo;s implement a Model Context Protocol (MCP) using the Micronaut framework.</p>
<p>We will use Micronaut 4.8 and Java 21.
You can find Micronaut&rsquo;s documentation here: <a href="https://docs.micronaut.io/4.8.11/guide/">https://docs.micronaut.io/4.8.11/guide/</a></p>
<p>The details of the Model Context Protocol (MCP) can be found here:
<a href="https://modelcontextprotocol.io/llms-full.txt">https://modelcontextprotocol.io/llms-full.txt</a></p>
<p>For the client, we will use LangChain4j as shown in this article:
<a href="https://glaforge.dev/posts/2025/04/04/mcp-client-and-server-with-java-mcp-sdk-and-langchain4j/">https://glaforge.dev/posts/2025/04/04/mcp-client-and-server-with-java-mcp-sdk-and-langchain4j/</a></p>
<p>You can find all the code of the LangChain4j MCP client support in the attached file.</p>
<p>[!INFO] Gitingest
&#x1f4ce; <em>[gitingest of the LangChain4j MCP client code]</em></p>
<p>You can reuse the Java classes of the LangChain4j MCP client to implement the MCP server support with Micronaut.</p>
<p>Implement a simple MCP server to let MCP clients request the weather forecast. Return fake data like <code>{&quot;forecast&quot;: &quot;sunny&quot;}</code></p></blockquote>
<p>The trick here was to feed the whole MCP specification thanks to the <code>llms-full.txt</code> file,
the whole Micronaut single-page documentation, and also the LangChain4j MCP client source code in attachment (via <a href="https://gitingest.com/">gitingest</a>).</p>
<p>Did it work in one shot? Actually, no.
Because my client wouldn&rsquo;t connect to it somehow, I figured that it wasn&rsquo;t actually using Server-Sent Events.
So I sent a follow-up prompt:</p>
<blockquote>
<p>The Micronaut controller must use HTTP Server Sent Events, as this is what the MCP protocol mandates for MCP remote servers. Please update the controller to use SSE.</p></blockquote>
<p>Then I had a running server.</p>
<p>Gemini created an <a href="https://github.com/glaforge/langchain4j-micronaut-mcp/blob/main/src/main/java/mcp/server/SseBroadcaster.java"><code>SseBroadcaster</code></a> class which handles the Server-Sent Event handling,
thanks to Reactor&rsquo;s <code>Publisher</code>, <code>Flux</code>, and <code>Sinks</code>, and Micronaut&rsquo;s <code>JsonMapper</code> and SSE support.</p>
<p>It handles the various JSON-RPC operations (<code>initialize</code>, <code>notifications/initialized</code>, <code>tools/list</code>, <code>tools/call</code>, and <code>ping</code>)
in the <a href="https://github.com/glaforge/langchain4j-micronaut-mcp/blob/main/src/main/java/mcp/server/PostController.java"><code>PostController</code></a>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span>McpResponse<span style="color:#bbb"> </span><span style="color:#06287e">processRequest</span>(McpRequest<span style="color:#bbb"> </span>request)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// --- Same logic as before to generate the McpResponse object ---</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">switch</span><span style="color:#bbb"> </span>(request.<span style="color:#4070a0">method</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;initialize&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Handling initialize request&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>InitializeResult<span style="color:#bbb"> </span>initResult<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>InitializeResult(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ServerCapabilities());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>McpResponse(request.<span style="color:#4070a0">id</span>(),<span style="color:#bbb"> </span>initResult);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;notifications/initialized&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Received initialized notification&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#60a0b0;font-style:italic">// This is a notification FROM the client. MCP spec says notifications</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#60a0b0;font-style:italic">// don&#39;t have responses. So we return null here, and the POST handler</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#60a0b0;font-style:italic">// will just return HTTP OK.</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;tools/list&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Handling tools/list request&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>ToolSpecificationData<span style="color:#bbb"> </span>weatherTool<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ToolSpecificationData(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>WEATHER_TOOL_NAME,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#4070a0">&#34;Gets the current weather forecast.&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>InputSchema(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span><span style="color:#4070a0">&#34;object&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;location&#34;</span>,<span style="color:#bbb"> </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#4070a0">&#34;type&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;string&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#4070a0">&#34;description&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Location to get the weather for&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>List.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;location&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span><span style="color:#007020;font-weight:bold">false</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>ListToolsResult<span style="color:#bbb"> </span>listResult<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ListToolsResult(List.<span style="color:#4070a0">of</span>(weatherTool));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>McpResponse(request.<span style="color:#4070a0">id</span>(),<span style="color:#bbb"> </span>listResult);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;tools/call&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Handling tools/call request&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(request.<span style="color:#4070a0">params</span>()<span style="color:#bbb"> </span><span style="color:#666">!=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span><span style="color:#666">&amp;&amp;</span><span style="color:#bbb"> </span>request.<span style="color:#4070a0">params</span>().<span style="color:#4070a0">has</span>(<span style="color:#4070a0">&#34;name&#34;</span>))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>String<span style="color:#bbb"> </span>toolName<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>request.<span style="color:#4070a0">params</span>().<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;name&#34;</span>).<span style="color:#4070a0">asText</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(WEATHER_TOOL_NAME.<span style="color:#4070a0">equals</span>(toolName))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Executing tool: {}&#34;</span>,<span style="color:#bbb"> </span>toolName);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>TextContentData<span style="color:#bbb"> </span>textContent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>TextContentData(FAKE_WEATHER_JSON);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>CallToolResult<span style="color:#bbb"> </span>callResult<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>CallToolResult(List.<span style="color:#4070a0">of</span>(textContent));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>McpResponse(request.<span style="color:#4070a0">id</span>(),<span style="color:#bbb"> </span>callResult);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>log.<span style="color:#4070a0">warn</span>(<span style="color:#4070a0">&#34;Unknown tool requested: {}&#34;</span>,<span style="color:#bbb"> </span>toolName);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>McpResponse(request.<span style="color:#4070a0">id</span>(),<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>McpError(<span style="color:#666">-</span>32601,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Method not found: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>toolName));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>log.<span style="color:#4070a0">error</span>(<span style="color:#4070a0">&#34;Invalid tools/call request: Missing &#39;name&#39; in params&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>McpResponse(request.<span style="color:#4070a0">id</span>(),<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>McpError(<span style="color:#666">-</span>32602,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Invalid params for tools/call&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;ping&#34;</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Handling ping request&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>McpResponse(request.<span style="color:#4070a0">id</span>(),<span style="color:#bbb"> </span>Collections.<span style="color:#4070a0">emptyMap</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">default</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>log.<span style="color:#4070a0">warn</span>(<span style="color:#4070a0">&#34;Unsupported MCP method: {}&#34;</span>,<span style="color:#bbb"> </span>request.<span style="color:#4070a0">method</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>McpResponse(request.<span style="color:#4070a0">id</span>(),<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>McpError(<span style="color:#666">-</span>32601,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Method not found: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>request.<span style="color:#4070a0">method</span>()));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="from-vibe-coding-to-a-more-classical-ai-assisted-approach">From vibe coding, to a more classical AI-assisted approach</h2>
<p>The vibe coding part ended somewhere here, as I then went on to make a few tweaks here and there to the code base.
When you&rsquo;re a developer, you can&rsquo;t resist tweaking a few things here and there, right?</p>
<p>But I continued the journey also with the help of Gemini, but via <a href="https://codeassist.google/">Gemini Code Assist</a> within IntelliJ IDEA.</p>
<p>I reused my MCP client from my recent <a href="https://glaforge.dev/posts/2025/04/04/mcp-client-and-server-with-java-mcp-sdk-and-langchain4j/">MCP article</a>,
but I asked Gemini Code Assist to transform the Java class into a proper JUnit test.
Since the Micronaut documentation is still part of the context of the conversation, thanks to Gemini&rsquo;s huge context window, it did again a great job at converting my code into a proper Micronaut running unit test, launching an embedded server.</p>
<p>I like that Gemini created three test methods: to test that the server can reply to some simple greeting message, then can list and the available MCP tools at the disposal of the clients:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Test</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">testListTools</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Testing listTools...&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>assertDoesNotThrow(()<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>List<span style="color:#666">&lt;</span>dev.<span style="color:#4070a0">langchain4j</span>.<span style="color:#4070a0">agent</span>.<span style="color:#4070a0">tool</span>.<span style="color:#4070a0">ToolSpecification</span><span style="color:#666">&gt;</span><span style="color:#bbb"> </span>tools<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>mcpClient.<span style="color:#4070a0">listTools</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>assertNotNull(tools,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Tool list should not be null&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>assertFalse(tools.<span style="color:#4070a0">isEmpty</span>(),<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Tool list should not be empty&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// Add more specific assertions if needed, e.g., check tool names</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>assertTrue(tools.<span style="color:#4070a0">stream</span>().<span style="color:#4070a0">anyMatch</span>(t<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;getWeatherForecast&#34;</span>.<span style="color:#4070a0">equals</span>(t.<span style="color:#4070a0">name</span>())),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;Should find the &#39;getWeatherForecast&#39; tool&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;listTools returned: {}&#34;</span>,<span style="color:#bbb"> </span>tools);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>},<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Listing tools should not throw an exception&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>And finally is able to reply to a weather request by returning some dummy weather data:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Test</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">testWeatherRequest</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Testing weather request...&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>question<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;What&#39;s the weather like in Paris today?&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>assertDoesNotThrow(()<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>weatherAssistant.<span style="color:#4070a0">request</span>(question),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Weather request should not throw an exception&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Question: {}&#34;</span>,<span style="color:#bbb"> </span>question);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>log.<span style="color:#4070a0">info</span>(<span style="color:#4070a0">&#34;Response: {}&#34;</span>,<span style="color:#bbb"> </span>response);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>assertNotNull(response,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Response should not be null&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>assertFalse(response.<span style="color:#4070a0">isBlank</span>(),<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Response should not be blank&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// Check if the response likely contains the mocked forecast</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>assertTrue(response.<span style="color:#4070a0">toLowerCase</span>().<span style="color:#4070a0">contains</span>(<span style="color:#4070a0">&#34;sunny&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Response should contain the weather information (sunny)&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="now-the-code">Now, the code!</h2>
<p>As you made it till the end, you&rsquo;ll get a reward: I pushed the code in this <a href="https://github.com/glaforge/langchain4j-micronaut-mcp">Github repository</a>!
The project doesn&rsquo;t implement all the bells and whistles of the MCP specification (like prompts, resources, sampling, etc.)
but it&rsquo;s certainly a starting point if you want to write your own MCP server with Micronaut.
Since LangChain4j released some <a href="https://github.com/langchain4j/langchain4j/releases/tag/1.0.0-rc1">new versions today</a>, I updated the dependencies to use the latest and greatest LangChain4j.</p>
<p>Going further, I&rsquo;d love to see Micronaut offer a dedicated MCP server module, to make it easier to implement MCP servers, with some clever annotations, to streamline the whole process.
Fingers crossed &#x1f91e;</p>
<p>And of course, once you&rsquo;re happy with your MCP server implementation, the extra step is to deploy the MCP server to Google&rsquo;s <a href="http://cloud.run/">Cloud Run</a>, like I explained in this article about the
<a href="https://glaforge.dev/posts/2022/10/24/build-deploy-java-17-apps-on-cloud-run-with-cloud-native-buildpacks-on-temurin/">various ways to deploy Micronaut apps to Cloud Run</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>MCP Client and Server with the Java MCP SDK and LangChain4j</title><link>https://glaforge.dev/posts/2025/04/04/mcp-client-and-server-with-java-mcp-sdk-and-langchain4j/</link><pubDate>Fri, 04 Apr 2025 19:39:58 +0200</pubDate><guid>https://glaforge.dev/posts/2025/04/04/mcp-client-and-server-with-java-mcp-sdk-and-langchain4j/</guid><description>&lt;p>&lt;a href="https://modelcontextprotocol.io/introduction">MCP&lt;/a> (Model Context Protocol) is making a buzz these days!
MCP is a protocol invented &lt;a href="https://www.anthropic.com/news/model-context-protocol">last November&lt;/a> by Anthropic,
integrated in Claude Desktop and in more and more tools and frameworks,
to &lt;strong>expand LLMs capabilities&lt;/strong> by &lt;strong>giving them access to various external tools&lt;/strong> and functions.&lt;/p>
&lt;blockquote>
&lt;p>My colleague &lt;a href="https://x.com/_philschmid">Philipp Schmid&lt;/a> gave a great
&lt;a href="https://www.philschmid.de/mcp-introduction">introduction to MCP&lt;/a> recently,
so if you want to learn more about MCP, this is the place for you.&lt;/p>&lt;/blockquote>
&lt;p>In this article, I&amp;rsquo;d like to guide you through the implementation of an MCP server, and an MCP client, in Java.
As I&amp;rsquo;m contributing to &lt;a href="https://docs.langchain4j.dev/">LangChain4j&lt;/a>, I&amp;rsquo;ll be using LangChain4j&amp;rsquo;s &lt;code>mcp&lt;/code> module for the client.&lt;/p></description><content:encoded>
<![CDATA[<p><a href="https://modelcontextprotocol.io/introduction">MCP</a> (Model Context Protocol) is making a buzz these days!
MCP is a protocol invented <a href="https://www.anthropic.com/news/model-context-protocol">last November</a> by Anthropic,
integrated in Claude Desktop and in more and more tools and frameworks,
to <strong>expand LLMs capabilities</strong> by <strong>giving them access to various external tools</strong> and functions.</p>
<blockquote>
<p>My colleague <a href="https://x.com/_philschmid">Philipp Schmid</a> gave a great
<a href="https://www.philschmid.de/mcp-introduction">introduction to MCP</a> recently,
so if you want to learn more about MCP, this is the place for you.</p></blockquote>
<p>In this article, I&rsquo;d like to guide you through the implementation of an MCP server, and an MCP client, in Java.
As I&rsquo;m contributing to <a href="https://docs.langchain4j.dev/">LangChain4j</a>, I&rsquo;ll be using LangChain4j&rsquo;s <code>mcp</code> module for the client.</p>
<p>For the server, it&rsquo;s possible to use <a href="https://quarkus.io/blog/mcp-server/">Quarkus</a> or
<a href="https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html">Spring Boot</a>.
But Christian Tsolov, who built the MCP library used by Spring Boot (which is also the official <a href="https://github.com/modelcontextprotocol/java-sdk">Java SDK</a> promoted by the Model Context Protocol project), recently <a href="https://x.com/christzolov/status/1906341689142243792">tweeted</a> that <strong>the MCP reference implementation can also be used standalone</strong> without a mandatory framework:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">🚀 The MCP Java SDK core module provides default STDIO &amp; SSE client/server transports - no external web frameworks needed!<br><br>Build MCP clients/servers in plain Java with both Async (Reactor) &amp; Sync APIs. Spring support is optional.<br><br>🔗 Minimal deps: <a href="https://t.co/9uW5vgMqqh">https://t.co/9uW5vgMqqh</a> <a href="https://t.co/aHLBHuXOUz">pic.twitter.com/aHLBHuXOUz</a></p>&mdash; Christian Tzolov🇧🇬🇪🇺🇺🇦 🦋@tzolov.bsky.social (@christzolov) <a href="https://twitter.com/christzolov/status/1906341689142243792?ref_src=twsrc%5Etfw">March 30, 2025</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


<h2 id="developing-the-mcp-server">Developing the MCP server</h2>
<p>For the server, I need the reference implementation dependency, as well as some Jetty JARs (or the servlet container of your choice)
to expose an HTTP Server-Sent Event endpoint (you can also create <code>stdio</code> servers too):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>io.modelcontextprotocol.sdk<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>mcp<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#062873;font-weight:bold">&lt;version&gt;</span>0.8.1<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>org.eclipse.jetty<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>jetty-server<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#062873;font-weight:bold">&lt;version&gt;</span>12.0.18<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>org.eclipse.jetty.ee10<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>jetty-ee10-servlet<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#062873;font-weight:bold">&lt;version&gt;</span>12.0.18<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div><p>I&rsquo;m using Jetty here, as I want to expose the HTTP SSE endpoint as a servlet.</p>
<p>The first thing needed is to create an HTTP servlet SSE transport provider.
I&rsquo;ll expose the <code>/sse</code> endpoint that the client will be able to access:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>HttpServletSseServerTransportProvider<span style="color:#bbb"> </span>transportProvider<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>HttpServletSseServerTransportProvider(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ObjectMapper(),<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;/&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;/sse&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>The MCP reference implementation allows you to implement async or sync servers.
I&rsquo;m going with a synchronous one, as it&rsquo;s easier to implement:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>McpSyncServer<span style="color:#bbb"> </span>syncServer<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>McpServer.<span style="color:#4070a0">sync</span>(transportProvider)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">serverInfo</span>(<span style="color:#4070a0">&#34;custom-server&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;0.0.1&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">capabilities</span>(McpSchema.<span style="color:#4070a0">ServerCapabilities</span>.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">tools</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">resources</span>(<span style="color:#007020;font-weight:bold">false</span>,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">false</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">prompts</span>(<span style="color:#007020;font-weight:bold">false</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>MCP servers can expose:</p>
<ul>
<li>tools</li>
<li>resources</li>
<li>prompts</li>
</ul>
<p>In my case, I&rsquo;m just interested in exposing a tool.
I&rsquo;ll go with a classical weather tool, which is a bit like the <em>Hello World</em> of LLM function calling!</p>
<p>Let&rsquo;s define our <code>weather-forecast</code> tool:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>McpServerFeatures.<span style="color:#4070a0">SyncToolSpecification</span><span style="color:#bbb"> </span>syncToolSpecification<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>McpServerFeatures.<span style="color:#4070a0">SyncToolSpecification</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>McpSchema.<span style="color:#4070a0">Tool</span>(<span style="color:#4070a0">&#34;weather-forecast&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;gives today&#39;s weather forecast for a given location&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            {
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">              &#34;type&#34;: &#34;object&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">              &#34;properties&#34;: {
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                &#34;location&#34;: {
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                  &#34;type&#34;: &#34;string&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                }
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">              },
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">              &#34;required&#34;: [&#34;location&#34;]
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            }
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;&#34;&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>(mcpSyncServerExchange,<span style="color:#bbb"> </span>stringObjectMap)<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>McpSchema.<span style="color:#4070a0">CallToolResult</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>List.<span style="color:#4070a0">of</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>McpSchema.<span style="color:#4070a0">TextContent</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                    {
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                        &#34;location&#34;: &#34;Paris&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                        &#34;forecast&#34;: &#34;Nice and sunny weather, with clear blue sky, and temperature of 17°C.&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                    }
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                    &#34;&#34;&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>)),<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">false</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>syncServer.<span style="color:#4070a0">addTool</span>(syncToolSpecification);<span style="color:#bbb">
</span></span></span></code></pre></div><p>I defined the tool, with a description (which helps LLMs know which tools to invoke for which use case).
The schema of the input is described as a JSON string (as I struggled a bit to find the correct way to create the schema programmatically).</p>
<p>Then I defined the lambda function that is called when the tool is invoked.
I&rsquo;m returning a JSON object that contains the <code>location</code> and the <code>forecast</code>.</p>
<p>And I&rsquo;m done with the MCP server implementation!</p>
<p>But now, I need to expose this server thanks to the Jetty Servlet container.</p>
<p>Let&rsquo;s define a new Jetty server, connector, servlet context handler, export the servlet, and start the server:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>QueuedThreadPool<span style="color:#bbb"> </span>threadPool<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>QueuedThreadPool();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>threadPool.<span style="color:#4070a0">setName</span>(<span style="color:#4070a0">&#34;server&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Server<span style="color:#bbb"> </span>server<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Server(threadPool);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ServerConnector<span style="color:#bbb"> </span>connector<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ServerConnector(server);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>connector.<span style="color:#4070a0">setPort</span>(45450);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>server.<span style="color:#4070a0">addConnector</span>(connector);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ServletContextHandler<span style="color:#bbb"> </span>context<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ServletContextHandler();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>context.<span style="color:#4070a0">setContextPath</span>(<span style="color:#4070a0">&#34;/&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>context.<span style="color:#4070a0">addServlet</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ServletHolder(transportProvider),<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;/*&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>server.<span style="color:#4070a0">setHandler</span>(context);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>server.<span style="color:#4070a0">start</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Now if you run this code, your MCP server function will be waiting for its first invocations.</p>
<h2 id="developing-the-mcp-client-with-langchain4j">Developing the MCP client with LangChain4j</h2>
<p>For the MCP client, I use the <a href="https://docs.langchain4j.dev/tutorials/mcp/">LangChain4j MCP module</a>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>dev.langchain4j<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>langchain4j-mcp<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#062873;font-weight:bold">&lt;version&gt;</span>${langchain4j.version}<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div><p>Since I&rsquo;m using Gemini, I need some dependencies for the Vertex AI Gemini model:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>dev.langchain4j<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>langchain4j<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>${langchain4j.version}<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>dev.langchain4j<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>langchain4j-vertex-ai-gemini<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>${langchain4j.version}<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div><p>Let&rsquo;s get started with instantiating our Gemini 2.0 Flash lite model:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>VertexAiGeminiChatModel<span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">project</span>(<span style="color:#4070a0">&#34;genai-playground24&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">location</span>(<span style="color:#4070a0">&#34;us-central1&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash-lite&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>I&rsquo;m defining an <code>McpTransport</code> pointing at my local Jetty SSE server:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>McpTransport<span style="color:#bbb"> </span>transport<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>HttpMcpTransport.<span style="color:#4070a0">Builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">sseUrl</span>(<span style="color:#4070a0">&#34;http://0.0.0.0:45450/sse&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span></code></pre></div><p>Let&rsquo;s create an MCP client using that transport:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>McpClient<span style="color:#bbb"> </span>mcpClient<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>DefaultMcpClient.<span style="color:#4070a0">Builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">transport</span>(transport)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>And a tool provider that will expose just the <code>weather-forecast</code> tool:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ToolProvider<span style="color:#bbb"> </span>toolProvider<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>McpToolProvider.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">mcpClients</span>(List.<span style="color:#4070a0">of</span>(mcpClient))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>You can list the available tools as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>mcpClient.<span style="color:#4070a0">listTools</span>().<span style="color:#4070a0">forEach</span>(System.<span style="color:#4070a0">out</span>::println);<span style="color:#bbb">
</span></span></span></code></pre></div><p>As I&rsquo;m going to create a LangChain4j AI service, I need a contract.
This will be the following simple interface:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">interface</span> <span style="color:#0e84b5;font-weight:bold">WeatherAssistant</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span><span style="color:#06287e">request</span>(String<span style="color:#bbb"> </span>message);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Now, it&rsquo;s time to instantiate that service:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>WeatherAssistant<span style="color:#bbb"> </span>meteo<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>AiServices.<span style="color:#4070a0">builder</span>(WeatherAssistant.<span style="color:#4070a0">class</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">chatLanguageModel</span>(model)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">toolProvider</span>(toolProvider)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>And we can now chat with that service, asking mundane questions, as well as weather related questions that will be invoking the <code>weather-forecast</code> tool:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>List.<span style="color:#4070a0">of</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;Hello!&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;What&#39;s the weather like in Paris today?&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>).<span style="color:#4070a0">forEach</span>((String<span style="color:#bbb"> </span>q)<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(blue(q));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(green(meteo.<span style="color:#4070a0">request</span>(q)));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>});<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>It will print something like:</p>
<pre tabindex="0"><code>Hello!
The weather in Paris is sunny today, with a clear blue sky,
and a temperature of 17°C.
</code></pre><h2 id="conclusion">Conclusion</h2>
<p>In this article, we&rsquo;ve seen:</p>
<ul>
<li>how to <strong>create an MCP server</strong>, using the official <strong>Java MCP SDK</strong>,</li>
<li>and how to <strong>create an MCP client</strong> with <strong>LangChain4j</strong>.</li>
</ul>
<p>We&rsquo;ve seen how to define a tool, and how to expose it via an HTTP SSE endpoint.
We&rsquo;ve also seen how to use the MCP client to invoke that tool, and how to integrate it in a LangChain4j AI service.</p>
<p>This is a great way to expand the capabilities of LLMs, by giving them access to external tools and functions.
And it shows that you can interact with any MCP server with LangChain4j.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Quick Tip: Clearing disk space in Cloud Shell</title><link>https://glaforge.dev/posts/2025/03/08/quick-tip-clearing-disk-space-in-cloud-shell/</link><pubDate>Sat, 08 Mar 2025 16:29:41 +0100</pubDate><guid>https://glaforge.dev/posts/2025/03/08/quick-tip-clearing-disk-space-in-cloud-shell/</guid><description>&lt;p>Right in the middle of a &lt;a href="https://glaforge.dev/posts/2024/03/27/gemini-codelab-for-java-developers/">workshop&lt;/a> I was delivering, as I was launching Google Cloud console&amp;rsquo;s &lt;a href="https://cloud.google.com/shell/docs">Cloud Shell&lt;/a> environment, I received the dreaded warning message: &lt;code>no space left on device&lt;/code>.&lt;/p>
&lt;p>And indeed, I didn&amp;rsquo;t have much space left, and Cloud Shell was reminding me it was high time I clean up the mess! Fortunately, the shell gives a nice hint, with a pointer to this &lt;a href="https://cloud.google.com/shell/docs/quotas-limits#clearing_disk_space">documentation page&lt;/a> with advice on how to reclaim space.&lt;/p></description><content:encoded>
<![CDATA[<p>Right in the middle of a <a href="https://glaforge.dev/posts/2024/03/27/gemini-codelab-for-java-developers/">workshop</a> I was delivering, as I was launching Google Cloud console&rsquo;s <a href="https://cloud.google.com/shell/docs">Cloud Shell</a> environment, I received the dreaded warning message: <code>no space left on device</code>.</p>
<p>And indeed, I didn&rsquo;t have much space left, and Cloud Shell was reminding me it was high time I clean up the mess! Fortunately, the shell gives a nice hint, with a pointer to this <a href="https://cloud.google.com/shell/docs/quotas-limits#clearing_disk_space">documentation page</a> with advice on how to reclaim space.</p>
<p>The document suggests to run the following command:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>du -hs <span style="color:#007020;font-weight:bold">$(</span>ls -A<span style="color:#007020;font-weight:bold">)</span>
</span></span></code></pre></div><p>This command shows the space each file uses within each sub-directory.</p>
<p>Here&rsquo;s the output I got after having cleaned up the many caches, directories and projects I didn&rsquo;t need anymore:</p>
<pre tabindex="0"><code>20K     .bash_history
4.0K    .bash_logout
4.0K    .bashrc
20M     .cache
320M    .codeoss
112K    .config
8.0K    .docker
247M    gemini-workshop-for-java-developers
4.0K    .gitconfig
341M    .gradle
12K     .gsutil
4.0K    .lesshst
16K     .npm
4.0K    .profile
0       .python_history
4.0K    README-cloudshell.txt
8.0K    .redhat
4.0K    .ssh
0       .sudo_as_admin_successful
8.0K    .vscode
</code></pre><p>You quickly see directories (like <code>.codeoss</code> or my <code>gemini-workshop-for-java-developers</code>) that fill up the most space, and you can go after each of those repositories and launch some <code>rm -Rf some-directory</code> commands here and there. Of course, pay attention to what you&rsquo;re going to delete, as this is irreversible!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>LLMs.txt to help LLMs grok your content</title><link>https://glaforge.dev/posts/2025/03/03/llms-txt-to-help-llms-grok-your-content/</link><pubDate>Mon, 03 Mar 2025 11:03:35 +0100</pubDate><guid>https://glaforge.dev/posts/2025/03/03/llms-txt-to-help-llms-grok-your-content/</guid><description>&lt;p>Since I started my career, I&amp;rsquo;ve been sharing what I&amp;rsquo;ve learned along the way in this blog.
It makes me happy when developers find solutions to their problems, or discover new things, thanks to articles I&amp;rsquo;ve written here.
So it&amp;rsquo;s important for me that readers are able to find those posts.
Of course, my blog is indexed by search engines, and people usually find about it from Google or other engines, or they discover it via the links I share on social media.
But with LLM powered tools (like Gemini, ChatGPT, Claude, etc.) you can make your content more easily &lt;em>grokkable&lt;/em> by such tools.&lt;/p></description><content:encoded>
<![CDATA[<p>Since I started my career, I&rsquo;ve been sharing what I&rsquo;ve learned along the way in this blog.
It makes me happy when developers find solutions to their problems, or discover new things, thanks to articles I&rsquo;ve written here.
So it&rsquo;s important for me that readers are able to find those posts.
Of course, my blog is indexed by search engines, and people usually find about it from Google or other engines, or they discover it via the links I share on social media.
But with LLM powered tools (like Gemini, ChatGPT, Claude, etc.) you can make your content more easily <em>grokkable</em> by such tools.</p>
<p>A <a href="https://llmstxt.org/">proposal</a> emerged last year to add a new file websites, <code>llms.txt</code>, which is a Markdown document that lists all the posts, pages, articles of that website. In a way, it&rsquo;s similar to other kind of indexing-related files like <code>robots.txt</code> or <code>sitemap.xml</code>. But this time, the idea is to offer the content of your documentation, website, or blog, as Markdown, easily <em>grokkable</em> by LLM powered tools.</p>
<h2 id="updating-hugo-to-generate-llmstxt-files">Updating Hugo to generate LLMs.txt files</h2>
<p>This blog post is powered by <a href="https://gohugo.io/">Hugo</a>, a static site generator.
I&rsquo;ll explain how I added 2 new files to my blog:</p>
<ul>
<li><a href="https://glaforge.dev/llms.txt">llms.txt</a> — which references links to all the blog posts, a bit like a sitemap.</li>
<li><a href="https://glaforge.dev/llms-full.txt">llms-full.txt</a> — which contains all the blog post articles in one giant file.</li>
</ul>
<p>In <code>config.toml</code>, I added two new output formats:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span>[outputFormats.llms]
</span></span><span style="display:flex;"><span>baseName = <span style="color:#4070a0">&#34;llms&#34;</span>
</span></span><span style="display:flex;"><span>mediaType = <span style="color:#4070a0">&#34;text/plain&#34;</span>
</span></span><span style="display:flex;"><span>isPlainText = <span style="color:#007020;font-weight:bold">true</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>[outputFormats.llms-full]
</span></span><span style="display:flex;"><span>baseName = <span style="color:#4070a0">&#34;llms-full&#34;</span>
</span></span><span style="display:flex;"><span>mediaType = <span style="color:#4070a0">&#34;text/plain&#34;</span>
</span></span><span style="display:flex;"><span>isPlainText = <span style="color:#007020;font-weight:bold">true</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>[outputs]
</span></span><span style="display:flex;"><span>home = [<span style="color:#4070a0">&#34;HTML&#34;</span>, <span style="color:#4070a0">&#34;RSS&#34;</span>, <span style="color:#4070a0">&#34;llms&#34;</span>, <span style="color:#4070a0">&#34;llms-full&#34;</span>]
</span></span></code></pre></div><p>And in my custom theme <code>layouts\_default</code> folder, I added two templates for those two files:</p>
<ul>
<li><code>index.llms.txt</code> references all the articles</li>
</ul>
<pre tabindex="0"><code># Blog of {{ .Site.Title }}

&gt; {{ .Site.Params.Description }}

You can find [more information about this site here](/about).

## All articles
{{ range .Site.RegularPages.GroupByDate &#34;2006&#34; }}

### {{ .Key }}
{{ range .Pages }}
- [{{- .Title -}}]({{- .Site.BaseURL -}}{{- .RelPermalink -}})
{{- end -}}
{{ end }}
</code></pre><ul>
<li><code>index.llms-full.txt</code> provides the full content of all the articles</li>
</ul>
<pre tabindex="0"><code>{{ range .Site.RegularPages }}
# {{ .Title }}

{{ .RawContent }}
{{- end -}}
</code></pre><h2 id="now-what-can-you-do-with-that">Now what can you do with that?</h2>
<p>In my recent article on <a href="https://glaforge.dev/posts/2025/02/15/the-power-of-large-context-windows-for-your-documentation-efforts/">the power of large context windows for your documentation efforts</a>) I mentioned that you could feed all your content into an LLM prompt and have a discussion with it. This is the same idea I&rsquo;m following here. Thanks to the full export of all my articles, I can ask questions about everything I&rsquo;ve written so far.</p>
<p>Let&rsquo;s go to <a href="https://aistudio.google.com/">Google AI Studio</a> and ask Gemini, and its huge token context window!</p>
<p><figure>
  <a href="#img-eb43d4f995ef4c8132b7d3c2485fe331">
    <img src="/img/gemini/llms-txt-blog-content.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-eb43d4f995ef4c8132b7d3c2485fe331">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/llms-txt-blog-content.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>When I asked about the main topics of this blog, the above screenshot shows Gemini&rsquo;s answer, highlighting my content about <a href="https://groovy-lang.org/">Apache Groovy</a>, Google Cloud Platform, generative AI, Web APIs, software development best practices.</p>
<p>Look at the number of tokens! <strong>My whole blog is about 682k tokens</strong>! Only Gemini could <em>grok</em> all of that and make sense of it.</p>
<p>As a follow up question, I also asked <em>&ldquo;Can you detail more the articles about Generative AI?&rdquo;</em>, and Gemini highlighted some of my recent articles and summarized them all:</p>
<blockquote>
<p>Okay, based on the file, here&rsquo;s a breakdown of the articles specifically about Generative AI:</p>
<ul>
<li><strong>The power of large context windows for your documentation efforts:</strong> This article explores using LLMs with large context windows (like Gemini) to chat with reference documentation, to assist in tasks like creating new modules for LangChain4j. It discusses using &ldquo;gitingest&rdquo; to convert a GitHub project into a large text file that can be fed to an LLM.</li>
<li><strong>A Generative AI Agent with a real declarative workflow:</strong> This piece compares an imperative Java-based AI agent with a declarative approach using Google Cloud Workflows. The agent generates short sci-fi stories, and the article details the pros and cons of each method.</li>
<li><strong>An AI agent to generate short sci-fi stories:</strong> This project details how to build an AI-powered short story generation agent using Java, LangChain4j, Gemini, and Imagen 3, deployed on Cloud Run jobs. It contrasts explicit vs. autonomous agent workflows.</li>
<li><strong>Analyzing trends and topics from Bluesky&rsquo;s Firehose with generative AI:</strong> This article describes a project that analyzes the Bluesky social media stream, using vector embeddings and clustering to identify trending topics, and using Gemini to generate summaries of these topics.</li>
<li><strong>Let&rsquo;s think with Gemini Flash 2.0&rsquo;s experimental thinking mode and LangChain4j:</strong> This article explores the new &ldquo;thinking mode&rdquo; of Gemini 2.0 Flash, using LangChain4j to solve riddles and multimodal problems.</li>
<li><strong>Detecting objects with Gemini 2.0 and LangChain4j:</strong> This article discusses using Gemini 2.0&rsquo;s object detection capabilities with LangChain4j to recognize playing cards in images.</li>
<li><strong>Semantic code search for Programming Idioms with LangChain4j and Vertex AI embedding models:</strong> Explores the use of Vertex AI embedding models for semantic code search on the Programming Idioms community website. It focuses on using code retrieval task types and LangChain4j for efficient retrieval of code examples.</li>
<li><strong>Redacting sensitive information when using Generative AI models:</strong> Details the importance of redacting Personally Identifiable Information (PII) before sending user messages to LLMs, using the Google Cloud Data Loss Prevention (DLP) API.</li>
<li><strong>Data extraction: The many ways to get LLMs to spit JSON content:</strong> This article reviews different techniques to make LLMs generate JSON output and extract data from text, using Gemini and LangChain4j.</li>
</ul>
<p>These articles demonstrate a clear focus on using various LLMs (primarily Gemini) with tools like LangChain4j and Google Cloud services to address different AI-related problems.</p></blockquote>
<p>Not only did it summarize all those recent articles, but I also like the final sentence that outlines what most of those articles have in common: They are primarily using the Gemini model, with the <a href="https://docs.langchain4j.dev/">LangChain4j framework</a>, and running on Google Cloud Platform.</p>
<p>When asked to dive deeper on what is so special about LangChain4j, for instance, then again it highlighted the key features of the framework (that I demonstrated in my articles):</p>
<blockquote>
<p>LangChain4j appears to be a central tool for building Generative AI solutions in the provided articles, so I collected a few pieces of information to explain what&rsquo;s so special about it:</p>
<ul>
<li><strong>Java-Centric Approach:</strong> LangChain4j is a Java library. In a field dominated by Python, LangChain4j is a key tool for Java developers to leverage generative AI without having to switch to a different programming language or environment.</li>
<li><strong>LLM Orchestration:</strong> LangChain4j facilitates the orchestration of different components in an AI pipeline, from loading documents and splitting them into chunks, to calculate their embedding vectors, storing them in vector databases, and retrieve them to be passed to the LLM, and the augment the LLM prompt with context from those vectors. This reduces the complexity of AI application development.</li>
<li><strong>Integration with Google Cloud Services:</strong> LangChain4j has built-in integrations with Google Cloud&rsquo;s Vertex AI, Google AI Gemini, and Google Cloud Storage. This allows Java developers to easily use Google&rsquo;s LLMs, embedding models, and data storage solutions.</li>
<li><strong>Extensible and Customizable:</strong> The library offers a way to plug custom logic and algorithms into the different steps, and to define how the information should be used to interact with the LLM.</li>
<li><strong>Higher-Level Abstractions:</strong> LangChain4j provides higher-level abstractions for interacting with LLMs, such as the <code>AiServices</code> abstraction that returns type-safe objects, which allows developers to manipulate real Java objects, in a type-safe manner, instead of dealing with raw JSON strings.</li>
<li><strong>Capabilities like Tool Calling, Context Window, and the like:</strong> It integrates new LLM capabilities to improve the quality of the AI powered application&rsquo;s responses.</li>
<li><strong>Metadata Handling</strong>: It eases the handling of metadata in text segments, including filtering based on metadata during the retrieval phase.</li>
</ul>
<p>In the context of the articles, LangChain4j simplifies the implementation of complex tasks like:</p>
<ul>
<li>Creating chatbots that can answer questions based on a specific knowledge base (RAG).</li>
<li>Analyzing trends in social media data.</li>
<li>Generating structured content (e.g., JSON) from unstructured text.</li>
<li>Implementing semantic code search.</li>
</ul>
<p>LangChain4j&rsquo;s key value is that it allows Java developers to more easily build, experiment with, and deploy Generative AI applications using their existing skills and infrastructure, while abstracting away much of the underlying complexity.</p></blockquote>
<h2 id="thoughts-and-summary">Thoughts and summary</h2>
<p>For the past months, I&rsquo;ve been exploring various Retrieval Augmented Generation (RAG) techniques to improve the quality of answers for questions on heaps of documents and data, often chunked and stored in vector databases. However, RAG hits some limits when you want to ask more general questions, or to analyze some content more deeply, by understanding it in its entirety.</p>
<p>That&rsquo;s where LLMs like Gemini with large context windows help, as they can figure out the fine print, the relationships between various sections, see common patterns across articles. I believe we should take the <strong>best of both worlds, by finding relevant documents with a hybrid search approach mixing keyword and semantic searches, but then feeding LLMs not only with extracts but with whole documents</strong>.</p>
<p>The <code>llms.txt</code> initiative is interesting, and I hope it&rsquo;ll be adopted further in the future.
It will make it simpler for users to more easily reason about documentation, blogs, etc. via LLMs.
As LLMs are getting bigger context windows, the discussion with whole docs or sites become richer, with full understanding of the context of the corpus of documents.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Pretty-print Markdown on the console</title><link>https://glaforge.dev/posts/2025/02/27/pretty-print-markdown-on-the-console/</link><pubDate>Thu, 27 Feb 2025 17:01:26 +0100</pubDate><guid>https://glaforge.dev/posts/2025/02/27/pretty-print-markdown-on-the-console/</guid><description>&lt;p>With Large Language Models loving to output Markdown responses, I&amp;rsquo;ve been wanting to display those Markdown snippets nicely in the console, when developing some LLM-powered apps and experiments.
At first, I thought I could use a Markdown parser library, and implement some kind of output formatter to display the text nicely, taking advantage of &lt;a href="https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797">ANSI color codes and formats&lt;/a>.
However it felt a bit over-engineered, so I thought &lt;em>&amp;ldquo;hey, why not just use some simple regular expressions!&amp;rdquo;&lt;/em> (and now you&amp;rsquo;ll tell me I have a second problem with regexes)&lt;/p></description><content:encoded>
<![CDATA[<p>With Large Language Models loving to output Markdown responses, I&rsquo;ve been wanting to display those Markdown snippets nicely in the console, when developing some LLM-powered apps and experiments.
At first, I thought I could use a Markdown parser library, and implement some kind of output formatter to display the text nicely, taking advantage of <a href="https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797">ANSI color codes and formats</a>.
However it felt a bit over-engineered, so I thought <em>&ldquo;hey, why not just use some simple regular expressions!&rdquo;</em> (and now you&rsquo;ll tell me I have a second problem with regexes)</p>
<p>In this blog post, I just want to share the few lines of code I&rsquo;ve added in a utility class to output Markdown content nicely.
It&rsquo;s not covering all the bells &amp; whistles of the Markdown syntax (in particular things like Github flavored extensions, like arrays, etc.) but it&rsquo;s good enough for my use case.</p>
<h2 id="markdown-syntax-highlighting-on-the-console">Markdown syntax highlighting on the console</h2>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">markdown</span>(String<span style="color:#bbb"> </span>md)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>md<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// Bold</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">replaceAll</span>(<span style="color:#4070a0">&#34;\\*\\*(.*?)\\*\\*&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;\u001B[1m$1\u001B[0m&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// Italic</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">replaceAll</span>(<span style="color:#4070a0">&#34;\\*(.*?)\\*&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;\u001B[3m$1\u001B[0m&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// Underline</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">replaceAll</span>(<span style="color:#4070a0">&#34;__(.*?)__&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;\u001B[4m$1\u001B[0m&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// Strikethrough</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">replaceAll</span>(<span style="color:#4070a0">&#34;~~(.*?)~~&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;\u001B[9m$1\u001B[0m&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// Blockquote</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">replaceAll</span>(<span style="color:#4070a0">&#34;(&gt; ?.*)&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;\u001B[3m\u001B[34m\u001B[1m$1\u001B[22m\u001B[0m&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// Lists (bold magenta number and bullet)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">replaceAll</span>(<span style="color:#4070a0">&#34;([\\d]+\\.|-|\\*) (.*)&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;\u001B[35m\u001B[1m$1\u001B[22m\u001B[0m $2&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// Block code (black on gray)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">replaceAll</span>(<span style="color:#4070a0">&#34;(?s)```(\\w+)?\\n(.*?)\\n```&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;\u001B[3m\u001B[1m$1\u001B[22m\u001B[0m\n\u001B[57;107m$2\u001B[0m\n&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// Inline code (black on gray)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">replaceAll</span>(<span style="color:#4070a0">&#34;`(.*?)`&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;\u001B[57;107m$1\u001B[0m&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// Headers (cyan bold)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">replaceAll</span>(<span style="color:#4070a0">&#34;(#{1,6}) (.*?)\n&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;\u001B[36m\u001B[1m$1 $2\u001B[22m\u001B[0m\n&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// Headers with a single line of text followed by 2 or more equal signs</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">replaceAll</span>(<span style="color:#4070a0">&#34;(.*?\n={2,}\n)&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;\u001B[36m\u001B[1m$1\u001B[22m\u001B[0m\n&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// Headers with a single line of text followed by 2 or more dashes</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">replaceAll</span>(<span style="color:#4070a0">&#34;(.*?\n-{2,}\n)&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;\u001B[36m\u001B[1m$1\u001B[22m\u001B[0m\n&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// Images (blue underlined)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">replaceAll</span>(<span style="color:#4070a0">&#34;!\\[(.*?)]\\((.*?)\\)&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;\u001B[34m$1\u001B[0m (\u001B[34m\u001B[4m$2\u001B[0m)&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// Links (blue underlined)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">replaceAll</span>(<span style="color:#4070a0">&#34;!?\\[(.*?)]\\((.*?)\\)&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;\u001B[34m$1\u001B[0m (\u001B[34m\u001B[4m$2\u001B[0m)&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><blockquote>
<p>This can easily be translated into other programming languages. Just be careful with the small differences in syntax of regular expressions.</p></blockquote>
<p>For the following Markdown text:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span><span style="color:#000080;font-weight:bold"># Main title
</span></span></span><span style="display:flex;"><span><span style="color:#000080;font-weight:bold"></span>
</span></span><span style="display:flex;"><span>Big title
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold">## Subtitle
</span></span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold"></span>
</span></span><span style="display:flex;"><span>Small title
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000080;font-weight:bold"># Bold and italic
</span></span></span><span style="display:flex;"><span><span style="color:#000080;font-weight:bold"></span>
</span></span><span style="display:flex;"><span>Some <span style="font-weight:bold">**bold text**</span>.
</span></span><span style="display:flex;"><span>Bits of <span style="font-style:italic">_italicized text_</span>.
</span></span><span style="display:flex;"><span>It&#39;s <span style="font-weight:bold">**underlined**</span>.
</span></span><span style="display:flex;"><span>And <span style="color:#a00000">~~striked through~~</span>.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold">## Links
</span></span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold"></span>
</span></span><span style="display:flex;"><span>A [<span style="color:#062873;font-weight:bold">link</span>](<span style="color:#4070a0">https://www.example.com</span>) to an article.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>![<span style="color:#062873;font-weight:bold">alt text</span>](<span style="color:#4070a0">image.jpg</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold">### Quoting
</span></span></span><span style="display:flex;"><span><span style="color:#800080;font-weight:bold"></span><span style="color:#007020;font-weight:bold">
</span></span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">&gt; </span><span style="font-style:italic">a quote of someone famous, potentially wrapping around multiple lines.
</span></span></span><span style="display:flex;"><span><span style="font-style:italic"></span>
</span></span><span style="display:flex;"><span><span style="color:#000080;font-weight:bold"># Lists
</span></span></span><span style="display:flex;"><span><span style="color:#000080;font-weight:bold"></span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">1.</span> First item
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">2.</span> Second item
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">3.</span> Third item
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">-</span> First item
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">-</span> Second item
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">-</span> Third item
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#000080;font-weight:bold"># Code
</span></span></span><span style="display:flex;"><span><span style="color:#000080;font-weight:bold"></span>
</span></span><span style="display:flex;"><span>Some inline <span style="color:#4070a0">`code`</span> inside a paragraph.
</span></span><span style="display:flex;"><span>Return type is <span style="color:#4070a0">`void`</span> and args are <span style="color:#4070a0">`String[]`</span>.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>A fenced code block:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#4070a0">```java
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Hello</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">main</span>(String<span style="color:#666">[]</span><span style="color:#bbb"> </span>args)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;Hello World!&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#4070a0">```</span>
</span></span></code></pre></div><p>On the console output, the above Markdow document would be rendered as follows:</p>
<p><figure>
  <a href="#img-42fc11d41b5353368ffcb5d24249b5d9">
    <img src="/img/misc/markdown-rendered-in-console.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-42fc11d41b5353368ffcb5d24249b5d9">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/markdown-rendered-in-console.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Do you like this Markdown syntax highlighting?</p>
<h2 id="bonus-points">Bonus points</h2>
<p>At first, in my utility class, I only had methods for adding some colors in my program outputs.
Even if you don&rsquo;t use Markdown, those touches of color can be useful to differentiate key parts of your output.</p>
<p>So I created some methods for wrapping text in ANSI codes:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">red</span>(String<span style="color:#bbb"> </span>msg)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;\u001B[31m&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>msg<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;\u001B[0m&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">green</span>(String<span style="color:#bbb"> </span>msg)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;\u001B[31m&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>msg<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;\u001B[0m&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">bold</span>(String<span style="color:#bbb"> </span>msg)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;\u001B[1m&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>msg<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;\u001B[0m&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">italic</span>(String<span style="color:#bbb"> </span>msg)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;\u001B[3m&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>msg<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;\u001B[0m&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//...</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>You can combine them like in <code>bold(green(msg))</code>, add more colors, or even write some fancy rainbow text!
As long as the output is still readable, it&rsquo;s all fair game!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Advanced RAG — Sentence Window Retrieval</title><link>https://glaforge.dev/posts/2025/02/25/advanced-rag-sentence-window-retrieval/</link><pubDate>Tue, 25 Feb 2025 17:01:50 +0100</pubDate><guid>https://glaforge.dev/posts/2025/02/25/advanced-rag-sentence-window-retrieval/</guid><description>&lt;p>Retrieval Augmented Generation (RAG) is a great way to expand the knowledge of Large Language Models to let them know about your own data and documents. With RAG, LLMs can ground their answers on the information your provide, which reduces the chances of hallucinations.&lt;/p>
&lt;p>Implementing RAG is fairly trivial with a framework like &lt;a href="https://docs.langchain4j.dev/tutorials/rag">LangChain4j&lt;/a>. However, the results may not be on-par with your quality expectations. Often, you&amp;rsquo;ll need to further tweak different aspects of the RAG pipeline, like the document preparation phase (in particular docs chunking), or the retrieval phase to find the best information in your vector database.&lt;/p></description><content:encoded>
<![CDATA[<p>Retrieval Augmented Generation (RAG) is a great way to expand the knowledge of Large Language Models to let them know about your own data and documents. With RAG, LLMs can ground their answers on the information your provide, which reduces the chances of hallucinations.</p>
<p>Implementing RAG is fairly trivial with a framework like <a href="https://docs.langchain4j.dev/tutorials/rag">LangChain4j</a>. However, the results may not be on-par with your quality expectations. Often, you&rsquo;ll need to further tweak different aspects of the RAG pipeline, like the document preparation phase (in particular docs chunking), or the retrieval phase to find the best information in your vector database.</p>
<p>In this first article (hopefully of a series on advanced RAG techniques) I&rsquo;d like to explore an approach that may yield better results: <strong>sentence window retrieval</strong>, inspired by the technique described in this <a href="https://www.linkedin.com/pulse/sentence-window-retrieval-optimizing-llm-performance-rutam-bhagat-v24of/">article</a>.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Info</span>
      </div>
      <div class="admonition-content">
        <p>I&rsquo;ve explored many techniques in my <a href="https://glaforge.dev/talks/2024/10/14/advanced-rag-techniques/">advanced RAG techniques</a> presentation, if you feel like discovering other techniques that we&rsquo;ll explore in more details in this series.</p>
      </div>
    </div><h2 id="lets-step-back-to-naive-chunking">Let&rsquo;s step back to naive chunking</h2>
<p>First, why do we even split documents in smaller chunks?
We split documents into chunks in RAG because:</p>
<ul>
<li>It&rsquo;s easier to find the specific, relevant piece of information within a smaller chunk than a huge document.</li>
<li>Large Language Models have limited memory. Chunks allow us to feed them just the necessary context, instead of overwhelming them with the whole document.</li>
<li>Smaller chunks lead to more precise retrieval, delivering more accurate answers.</li>
</ul>
<p>The naive approach is to split in chunks of a certain amount of characters. For example, on the Wikipedia page of Berlin, a 100-character split might look as follows:</p>
<p><figure>
  <a href="#img-6e8c69d8a25a65da2b42a1f581e02c71">
    <img src="/img/rag/naive-chunk-1.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-6e8c69d8a25a65da2b42a1f581e02c71">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/rag/naive-chunk-1.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>If a user asks the question <em>&ldquo;What is the population of Berlin?&rdquo;</em>, the number of inhabitants is split across two chunks. So neither the first, nor the second chunk would yield the correct information, for the LLM to generate an accurate answer.</p>
<p>An obvious improvement is to use overlapping chunks:</p>
<p><figure>
  <a href="#img-91622a57a4c116c7860a8af0a47317a5">
    <img src="/img/rag/naive-chunk-2.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-91622a57a4c116c7860a8af0a47317a5">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/rag/naive-chunk-2.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>The red chunk and the orange chunk overlap: both contain the gray part as well. Which means that the second chunk contains the number (in full) we&rsquo;re interesteded in.</p>
<p>Another possible approach, to avoid splits and overlaps, is to chunk by sentences. After all, human beings write sentences for a good reason, because they bear information that represent a unit of semantic meaning.</p>
<p><figure>
  <a href="#img-5332a995f5f4be97256bd0ce94b47976">
    <img src="/img/rag/naive-chunk-3.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-5332a995f5f4be97256bd0ce94b47976">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/rag/naive-chunk-3.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>However, both the chunk with overlap example above, as well as the sentence split expose another flaw: Notice that the pronoun <code>its</code>, in the second chunk or the second sentence doesn&rsquo;t carry the information that it actually references <code>Berlin</code>. So the pronoun misses an important aspect of the sentence: this is a sentence about the population of Berlin. Not any other city.</p>
<p>An alternative may be to increase the size of the chunk, and/or the size of the overlap, to avoid information to be split across chunks (like the population figure), and to give more context about possible links between sentences (like our pronoun-city). However, the wider the chunks, the more diluted the semantic meaning in the resulting vector embeddings.</p>
<p>With more dillution, it&rsquo;s harder to have query vectors (the user prompt) match the chunks of texts with high similarity values.</p>
<h2 id="enters-sentence-window-retrieval">Enters sentence window retrieval</h2>
<p>The name of the technique comes from this <a href="https://www.linkedin.com/pulse/sentence-window-retrieval-optimizing-llm-performance-rutam-bhagat-v24of/">article</a> I mentioned.
But maybe it&rsquo;s not the best name we could find. Maybe something like <em>wider-context-sliding-window-embedding</em> would be more explicit, but that&rsquo;s a mouthful!</p>
<p>Let&rsquo;s have a look at this approach:</p>
<p><figure>
  <a href="#img-68511214847d786ce011edf6786e476a">
    <img src="/img/rag/naive-chunk-4.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-68511214847d786ce011edf6786e476a">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/rag/naive-chunk-4.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>The idea is as follows:</p>
<ul>
<li>We calculate vector embeddings for the sentence in dark green.</li>
<li>But we save the surrounding sentences in light green (for example, one sentence before, and two after).</li>
</ul>
<p>At retrieval time, the vector similarity calculation will match better with the dark green sentence (in spite of its missing <em>Berlin</em> aspect).
But the whole light + dark green context will be added in the prompt of the LLM, instead of the single sentence.</p>
<p>The advantages are that:</p>
<ul>
<li>We keep on carrying meaningful units of meaning with a few sentences, thus avoiding any key information cut between splits, and semantic dillution of bigger chunks.</li>
<li>It helps the LLM resolve links between pronouns and their related entity. The LLM knows that we&rsquo;re talking about Berlin here.</li>
</ul>
<h2 id="the-canonical-rag-implementation-in-langchain4j">The canonical RAG implementation in LangChain4j</h2>
<p>With LangChain4j, the base approach is as follows.
Let&rsquo;s start with the <strong>ingestion phase</strong>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Load the document (the Wikipedia page about Berlin)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Document<span style="color:#bbb"> </span>capitalDocument<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Document.<span style="color:#4070a0">from</span>(text);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Define an embedding model to calculate vector embeddings,</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// both for the text of the article, and for the user queries</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>embeddingModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiEmbeddingModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">endpoint</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_VERTEXAI_ENDPOINT&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">publisher</span>(<span style="color:#4070a0">&#34;google&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;text-embedding-005&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Store the chunks and their vectors in a vector database</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// (in this example, we&#39;ll use a simple in-memory store)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>embeddingStore<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>InMemoryEmbeddingStore<span style="color:#666">&lt;</span>TextSegment<span style="color:#666">&gt;</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Ingest the document in chunks of 100 characters</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// with an overlap of 20 characters,</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// use the in-memory vector store,</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// and the embedding model for vector calculations</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>EmbeddingStoreIngestor.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">documentSplitter</span>(DocumentSplitters.<span style="color:#4070a0">recursive</span>(100,<span style="color:#bbb"> </span>20))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">embeddingStore</span>(embeddingStore)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">embeddingModel</span>(embeddingModel)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">ingest</span>(capitalDocument);<span style="color:#bbb">
</span></span></span></code></pre></div><p>This is the naive approach using chunks of 100 characters with overlap.
Let&rsquo;s see what it looks like during the <strong>retrieval phase</strong>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Declare the LLM model we want to use</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>VertexAiGeminiChatModel<span style="color:#bbb"> </span>chatModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash-001&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Create an interface contract</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// that LangChain4j will implement for us</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">interface</span> <span style="color:#0e84b5;font-weight:bold">CapitalsAssistant</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Result<span style="color:#666">&lt;</span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span><span style="color:#06287e">learnAboutCapitals</span>(String<span style="color:#bbb"> </span>query);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// AiServices implements the interface</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// and binds the LLM, and a content retriever</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// that links the embedding model and vector store</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>CapitalsAssistant<span style="color:#bbb"> </span>assistant<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>AiServices.<span style="color:#4070a0">builder</span>(CapitalsAssistant.<span style="color:#4070a0">class</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">chatLanguageModel</span>(chatModel)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">contentRetriever</span>(EmbeddingStoreContentRetriever.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">embeddingModel</span>(embeddingModel)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">embeddingStore</span>(embeddingStore)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Now we can ask questions</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Result<span style="color:#666">&lt;</span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>assistant.<span style="color:#4070a0">learnAboutCapitals</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;How many inhabitants live in Berlin?&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>We could also add a memory component, to keep track of the ongoing discussion, it&rsquo;s just one extra line. But here, I stick to just single user questions.</p>
<h2 id="lets-implement-the-sentence-window-retrieval">Let&rsquo;s implement the sentence window retrieval</h2>
<p>Now, how can we expand the above code to implement the algorithm?</p>
<p>We need to split the text in sentences, and keep track of the surrounding sentences, as a sliding window, to give extra context to the LLM.
We can store that information as metadata of each text segment.
We must prepare the LLM prompt by inserting the surrounding context, instead of single sentences.</p>
<p>At ingestion phase, we can plug a <code>TextSegmentTransformer</code> that transforms our text chunks, to compute and store the surrounding context in the text segment metadata. We need to override both <code>transform()</code> and <code>transformAll()</code> methods, because we need to modify all chunks together (to get the surrounding sentences):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>EmbeddingStoreIngestor<span style="color:#bbb"> </span>ingestor<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>EmbeddingStoreIngestor.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>.<span style="color:#4070a0">documentSplitter</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>DocumentBySentenceSplitter(200,<span style="color:#bbb"> </span>20))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>.<span style="color:#4070a0">embeddingStore</span>(embeddingStore)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>.<span style="color:#4070a0">embeddingModel</span>(embeddingModel)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>.<span style="color:#4070a0">textSegmentTransformer</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>TextSegmentTransformer()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>TextSegment<span style="color:#bbb"> </span><span style="color:#06287e">transform</span>(TextSegment<span style="color:#bbb"> </span>segment)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>transformAll(Collections.<span style="color:#4070a0">singletonList</span>(segment))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">getFirst</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>List<span style="color:#666">&lt;</span>TextSegment<span style="color:#666">&gt;</span><span style="color:#bbb"> </span><span style="color:#06287e">transformAll</span>(List<span style="color:#666">&lt;</span>TextSegment<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>segments)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>List<span style="color:#666">&lt;</span>TextSegment<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>list<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ArrayList<span style="color:#666">&lt;&gt;</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(<span style="color:#902000">int</span><span style="color:#bbb"> </span>i<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>0;<span style="color:#bbb"> </span>i<span style="color:#bbb"> </span><span style="color:#666">&lt;</span><span style="color:#bbb"> </span>segments.<span style="color:#4070a0">size</span>();<span style="color:#bbb"> </span>i<span style="color:#666">++</span>)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>TextSegment<span style="color:#bbb"> </span>textSegment<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>segments.<span style="color:#4070a0">get</span>(i);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// Create a sliding window of sentences to gather</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// the context surrounding the embedded sentence</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// (2 sentences before, 3 after,</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// but you could make it configurable)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>context<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>IntStream.<span style="color:#4070a0">rangeClosed</span>(i<span style="color:#bbb"> </span><span style="color:#666">-</span><span style="color:#bbb"> </span>2,<span style="color:#bbb"> </span>i<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>3)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">filter</span>(j<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>j<span style="color:#bbb"> </span><span style="color:#666">&gt;=</span><span style="color:#bbb"> </span>0<span style="color:#bbb"> </span><span style="color:#666">&amp;&amp;</span><span style="color:#bbb"> </span>j<span style="color:#bbb"> </span><span style="color:#666">&lt;</span><span style="color:#bbb"> </span>segments.<span style="color:#4070a0">size</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">mapToObj</span>(j<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>segments.<span style="color:#4070a0">get</span>(j).<span style="color:#4070a0">text</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">collect</span>(Collectors.<span style="color:#4070a0">joining</span>(<span style="color:#4070a0">&#34; &#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// Store the surrounding context as metadata</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// of the text segment (the current chunk)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Metadata<span style="color:#bbb"> </span>metadata<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Metadata(textSegment.<span style="color:#4070a0">metadata</span>().<span style="color:#4070a0">toMap</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>metadata.<span style="color:#4070a0">put</span>(METADATA_CONTEXT_KEY,<span style="color:#bbb"> </span>context);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>list.<span style="color:#4070a0">add</span>(TextSegment.<span style="color:#4070a0">from</span>(textSegment.<span style="color:#4070a0">text</span>(),<span style="color:#bbb"> </span>metadata));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>list;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>})<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>That&rsquo;s a bit of code, but I hope to contribute an implementation to LangChain4j directly, so that you don&rsquo;t have to write this algorithm each time you want to apply it.</p>
<p>Let&rsquo;s focus now on the retrieval phase now, because we need to inject the surrounding context in the LLM prompt, instead of the sentence chunk itself. We need to create a <code>RetrievalAugmentor</code>, and configure the <code>ContentRetriever</code> we used before, and a <code>ContentInjector</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>CapitalsAssistant<span style="color:#bbb"> </span>assistant<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>AiServices.<span style="color:#4070a0">builder</span>(CapitalsAssistant.<span style="color:#4070a0">class</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">chatLanguageModel</span>(chatModel)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">retrievalAugmentor</span>(DefaultRetrievalAugmentor.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// the content retriever is defined</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// at the level of the retrieval augmentor</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">contentRetriever</span>(EmbeddingStoreContentRetriever.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">embeddingModel</span>(embeddingModel)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">embeddingStore</span>(embeddingStore)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// We create a content injector that injects</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// the surrounding context in the LLM prompt</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">contentInjector</span>((contents,<span style="color:#bbb"> </span>userMessage)<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#60a0b0;font-style:italic">// Retrieves the surrounding sentences</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#60a0b0;font-style:italic">// from the text segment&#39;s metadata</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>String<span style="color:#bbb"> </span>excerpts<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>contents.<span style="color:#4070a0">stream</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">map</span>(content<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>content<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">textSegment</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">metadata</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">getString</span>(METADATA_CONTEXT_KEY))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">collect</span>(Collectors.<span style="color:#4070a0">joining</span>(<span style="color:#4070a0">&#34;\n\n&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#60a0b0;font-style:italic">// Customize the prompt for our geography use case</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>PromptTemplate.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You are a helpful geography assistant
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        knowing everything about the capitals of the world.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Here&#39;s the question from the user:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &lt;question&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {{userMessage}}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &lt;/question&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Answer the question using the following information:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &lt;excerpts&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {{contents}}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &lt;/excerpts&gt;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>).<span style="color:#4070a0">apply</span>(Map.<span style="color:#4070a0">of</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#4070a0">&#34;userMessage&#34;</span>,<span style="color:#bbb"> </span>userMessage.<span style="color:#4070a0">singleText</span>(),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#4070a0">&#34;contents&#34;</span>,<span style="color:#bbb"> </span>excerpts<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>)).<span style="color:#4070a0">toUserMessage</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>})).<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Again, that&rsquo;s a bit of code, but we can make it reusable easily if needed in different contexts.</p>
<h2 id="summary">Summary</h2>
<p>With this <em>sentence window retrieval</em> approach, we calculate and store the vector embedding of a sentence, but we inject a wider surrounding context (a few sentences before and after) into the context of the LLM to generate its response with more information than just the single sentence. This tends to avoid the problem of key pieces of information cut in the middle, and to resolve references between sentences (like a pronoun pointing at a named entity defined earlier).</p>
<p>It&rsquo;s a technique worth experimenting with, to see if it gives better results in your own scenario.
However, before blindly applying a particular technique, be sure to prepare some evaluations: Measure the quality of your RAG pipeline before making changes. Then, measure after having applied a new technique, to see if the answers are better.</p>
<p>We&rsquo;ll have to explore the topic of evaluation another day, but in the meantime, I encourage you to read the blog posts of my colleague Mete Atamel who covered <a href="https://atamel.dev/posts/2025/01-09_evaluating_rag_pipelines/">RAG pipeline evaluation</a>, the <a href="https://atamel.dev/posts/2025/01-14_rag_evaluation_deepeval/">DeepEval</a> tool, and the <a href="https://atamel.dev/posts/2025/01-21_improve-rag-with-rag-triad-metrics/">RAG triad metric</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>The power of large context windows for your documentation efforts</title><link>https://glaforge.dev/posts/2025/02/15/the-power-of-large-context-windows-for-your-documentation-efforts/</link><pubDate>Sat, 15 Feb 2025 13:55:41 +0100</pubDate><guid>https://glaforge.dev/posts/2025/02/15/the-power-of-large-context-windows-for-your-documentation-efforts/</guid><description>&lt;p>My colleague Jaana Dogan was &lt;a href="https://x.com/rakyll/status/">pointing&lt;/a> at the Anthropic&amp;rsquo;s MCP (Model Context Protocol) documentation pages which were describing &lt;a href="https://modelcontextprotocol.io/tutorials/building-mcp-with-llms">how to build MCP servers and clients&lt;/a>.
The interesting twist was about &lt;em>preparing the documentation&lt;/em> in order to have Claude &lt;em>assist you&lt;/em> in building those MCP servers &amp;amp; clients, &lt;em>rather than clearly documenting how to do so&lt;/em>.&lt;/p>
&lt;blockquote class="twitter-tweet">&lt;p lang="en" dir="ltr">MCP tutorials are great. There are no tutorials really. &lt;br>&lt;br>&amp;quot;Copy these resources to Claude, and start asking some questions like...&amp;quot; &lt;a href="https://t.co/GG50DMWNLW">pic.twitter.com/GG50DMWNLW&lt;/a>&lt;/p></description><content:encoded>
<![CDATA[<p>My colleague Jaana Dogan was <a href="https://x.com/rakyll/status/">pointing</a> at the Anthropic&rsquo;s MCP (Model Context Protocol) documentation pages which were describing <a href="https://modelcontextprotocol.io/tutorials/building-mcp-with-llms">how to build MCP servers and clients</a>.
The interesting twist was about <em>preparing the documentation</em> in order to have Claude <em>assist you</em> in building those MCP servers &amp; clients, <em>rather than clearly documenting how to do so</em>.</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">MCP tutorials are great. There are no tutorials really. <br><br>&quot;Copy these resources to Claude, and start asking some questions like...&quot; <a href="https://t.co/GG50DMWNLW">pic.twitter.com/GG50DMWNLW</a></p>&mdash; Jaana Dogan ヤナ ドガン (@rakyll) <a href="https://twitter.com/rakyll/status/1890521760690270409?ref_src=twsrc%5Etfw">February 14, 2025</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


<p>No more tutorials:</p>
<ul>
<li><strong>You discuss with the reference documentation.</strong></li>
<li><strong>Your chat with the LLM becomes the tutorial!</strong></li>
</ul>
<p>LLM-powered documentation chats become your tailored tutorial, for your very own specific need and requirements.
Not only LLMs can assist you authoring articles, documentation, reports, but <strong>LLMs can craft explanations that help you achieve a particular goal for which there&rsquo;s not already a tutorial or how-to guide available</strong>.</p>
<p>Also, sometimes, you overlook some key paragraph of section when browsing through the documentation, and you miss the key information that would have helped you fix the problem at hand. This happened to me recently while using an Obsidian plugin: I needed to configure the plugin in a certain way, and I had the impression it wasn&rsquo;t possible, but the plugin author pointed me at the key paragraph that I somehow glanced over. <strong>Had I asked the question to an LLM-powered chat that has all the reference documentation in its context, maybe it would have found that paragraph I missed?</strong></p>
<h2 id="here-come-the-large-context-window-llms">Here come the large context window LLMs&hellip;</h2>
<p>As you may already know, I contribute to the <a href="https://docs.langchain4j.dev/">LangChain4j</a> open source project, which provides integrations with various LLMs (like Gemini) or vector databases. I hope to start working on an additional module to integrate the new <em>unified</em> <a href="https://github.com/googleapis/java-genai">Gemini SDK</a>.
The advantage of this new SDK is that you can call both Gemini <em>flavors</em>: the one provided by Google Cloud&rsquo;s Vertex AI offering, as well as DeepMind&rsquo;s Google AI version.
One SDK to rule them all!</p>
<p>I&rsquo;m also interested in potentially creating a new vector store module for Google <a href="https://cloud.google.com/firestore/docs/vector-search">Cloud Firestore</a>, which recently added vector calculation support in its Java client library. It would be neat to be able to use Firestore for RAG (Retrieval Augmented Generation) scenarios, taking advantage of the document database, its filtering capabilities, and its ability to do vector similarity searches.</p>
<p>LangChain4j&rsquo;s documentation provides some explanations on how to contribute new integrations, but I was interested in trying this approach of chatting with the reference documentation to guide my steps in creating a new LLM module, and a new vector store implementation module.</p>
<p>Fortunately, my favorite large language model, <a href="https://gemini.google.com/app">Gemini</a>, has a <strong>huge context window</strong> of <a href="https://developers.googleblog.com/en/gemini-2-family-expands/">up to 2 million tokens</a>!
This should be enough to ingurgitate the whole sources and documentation pages of the project.</p>
<p>Additionally, I discovered <code>gitingest</code> a great <a href="https://gitingest.com/">online tool</a> that allows you to <strong>convert a Github project and all its sources into one gigantic text file</strong>&hellip; that you can then feed to an LLM. Basically, you take the URL of a Github repository, and you replace the <code>github.com</code> part with <code>gitingest.com</code>. For example, for LangChain4j, the URL becomes: <code>https://gitingest.com/langchain4j/langchain4j</code>. Then, a download button lets you get the whole content of the repository.</p>
<h2 id="the-prompt">The prompt</h2>
<p>For this experiment, I didn&rsquo;t code anything, I simply used <a href="https://aistudio.google.com/app/prompts/new_chat">Google AI Studio</a>.</p>
<p>I selected <code>Gemini 2.0 Pro experimental</code>:</p>
<p><figure>
  <a href="#img-16e7f8b6b2de31376d0d2b387f16d9ce">
    <img src="/img/gemini/gemini-2.0-pro-exp.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-16e7f8b6b2de31376d0d2b387f16d9ce">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/gemini-2.0-pro-exp.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Then, I loaded the big text file of the LangChain4j project, created by gitingest, and look at the number of tokens:</p>
<p><figure>
  <a href="#img-8584e180376ac4745f717292d8c34c34">
    <img src="/img/gemini/2-million-tokens.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-8584e180376ac4745f717292d8c34c34">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/2-million-tokens.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Yes, the whole repository and documentation is almost 2 million tokens long!
Only Gemini can ingest that many tokens!</p>
<p>Time to ask my question:</p>
<blockquote>
<p>I&rsquo;d like to create a new language module for LangChain4j. How should I do that? What are the key steps to follow to propose a new model?</p></blockquote>
<h2 id="the-response">The response</h2>

<details>
  <summary>Click to reveal the detailed answer</summary>
  <p>Okay, let&rsquo;s outline the steps to create a new language model module for LangChain4j and propose its inclusion. Based on the provided file structure, you&rsquo;ll be focusing on creating a new module similar to the existing ones (e.g., <code>langchain4j-open-ai</code>, <code>langchain4j-ollama</code>, etc.). Here&rsquo;s a breakdown of the process, referencing the structure you&rsquo;ve provided:</p>
<p><strong>Key Steps and Considerations</strong></p>
<ol>
<li>
<p><strong>Understand the Abstractions and SPI:</strong> LangChain4j, like its Python counterpart, is built around core abstractions. You need to understand these to implement your integration correctly. The core abstractions you <em>must</em> implement are:</p>
<ul>
<li><strong><code>ChatLanguageModel</code> / <code>StreamingChatLanguageModel</code>:</strong> For conversational models (like ChatGPT, Gemini). Implement <code>ChatLanguageModel</code> for synchronous responses, and <code>StreamingChatLanguageModel</code> if the model supports streaming responses token by token.</li>
<li><strong><code>LanguageModel</code> / <code>StreamingLanguageModel</code>:</strong> For models with a simpler text-in, text-out interface (less common these days).</li>
<li><strong><code>EmbeddingModel</code>:</strong> If the model provider offers embedding capabilities.</li>
<li><strong><code>ModerationModel</code>:</strong> If the model provider offers content moderation.</li>
<li><strong><code>ScoringModel</code>:</strong> If the model provider offers scoring/ranking capabilities.</li>
<li><strong>Builder Factories:</strong> You&rsquo;ll also need to create builder factories (SPIs) for each model type you implement. These are how users will construct your model classes. See examples like <code>AzureOpenAiChatModelBuilderFactory</code>. These are registered using the Java ServiceLoader mechanism (the <code>META-INF/services</code> files).</li>
</ul>
</li>
<li>
<p><strong>Choose a Module Structure (and Repository):</strong></p>
<ul>
<li><strong>Community Repo (Preferred for new integrations):</strong> Start your integration in the <a href="https://github.com/langchain4j/langchain4j-community"><code>langchain4j-community</code></a> repository. This is the recommended approach for new contributions. It allows for easier initial review and iteration before considering a move to the core <code>langchain4j</code> repository. Clone this repo, don&rsquo;t fork the main <code>langchain4j</code> repo directly.</li>
<li><strong>Main <code>langchain4j</code> Repo (For Core Integrations):</strong> If your integration is with a very widely used and well-established model provider (like OpenAI, Google, etc.), <em>and</em> you are confident in its stability and long-term maintenance, you <em>might</em> propose it for the main repo. However, start in <code>langchain4j-community</code> first.</li>
<li><strong>Module Naming:</strong> Follow the pattern: <code>langchain4j-{provider-name}</code> (e.g., <code>langchain4j-my-llm</code>).</li>
<li><strong>Directory Structure:</strong> Create a directory structure mirroring the existing modules (see <code>langchain4j-open-ai</code> or <code>langchain4j-ollama</code> as good examples):
<pre tabindex="0"><code>langchain4j-{provider-name}/
    pom.xml  (Your module&#39;s Maven build file)
    src/
        main/
            java/
                dev/
                    langchain4j/
                        model/
                            {providername}/  (e.g., myllm)
                                {ProviderName}ChatModel.java  (Your implementation)
                                internal/ (API client and related classes)
                                spi/      (Builder factory for your model)
                                    {ProviderName}ChatModelBuilderFactory.java
            resources/
                META-INF/
                    services/
                        (Files to register your builder factory, see examples)
        test/
            java/
                dev/
                    langchain4j/
                        model/
                            {providername}/
                                {ProviderName}ChatModelIT.java (Integration tests)
</code></pre></li>
</ul>
</li>
<li>
<p><strong>Implement the API Client:</strong></p>
<ul>
<li><strong>Official SDK (Preferred):</strong> If the LLM provider has an official Java SDK, use it. This is usually the best approach for stability, performance, and access to all features. See <code>langchain4j-bedrock</code> for an example using an official SDK.</li>
<li><strong>HTTP Client (If no SDK):</strong> If there&rsquo;s no official SDK, use the JDK&rsquo;s built-in <code>java.net.http.HttpClient</code> (available since Java 11). This minimizes external dependencies. Avoid adding new dependencies unless absolutely necessary. See <code>http-clients/langchain4j-http-client-jdk</code> for how LangChain4j wraps this. Avoid using the older <code>okhttp3</code> directly if possible, prefer <code>langchain4j-http-client-jdk</code> (or <code>langchain4j-http-client-spring-restclient</code> if building a Spring Boot starter).</li>
<li><strong>JSON Handling:</strong> Use Jackson for JSON serialization/deserialization, as it&rsquo;s already a dependency.</li>
<li><strong>Error Handling:</strong> Make sure to handle HTTP errors (non-2xx responses) appropriately. Throw a <code>dev.langchain4j.exception.HttpException</code> for these.</li>
<li><strong>Request/Response Logging:</strong> Implement logging for requests and responses (see <code>langchain4j-anthropic</code> for a complete example). This is <em>very</em> helpful for debugging.</li>
</ul>
</li>
<li>
<p><strong>Implement the Model Interface(s):</strong></p>
<ul>
<li>Implement <code>ChatLanguageModel</code>, <code>StreamingChatLanguageModel</code>, <code>EmbeddingModel</code>, etc., as appropriate, based on the provider&rsquo;s capabilities.</li>
<li>Use the <code>Builder</code> pattern for your model classes to allow for flexible configuration.</li>
<li>Make sure your implementation handles request/response mapping and error handling correctly.</li>
<li>Implement <code>TokenCountEstimator</code> if possible, so the <code>TokenWindowChatMemory</code> can calculate the token usage.
Implement <code>DimensionAwareEmbeddingModel</code> to report the output dimension from the embedding model.</li>
</ul>
</li>
<li>
<p><strong>Write Tests:</strong></p>
<ul>
<li><strong>Unit Tests:</strong> Create unit tests for any complex logic, utility methods, and request/response mappers.</li>
<li><strong>Integration Tests (ITs):</strong> Create integration tests (e.g., <code>MyLlmChatModelIT.java</code>) that interact with the <em>real</em> LLM provider&rsquo;s API. These are <em>crucial</em> for ensuring your integration works correctly.
<ul>
<li>Use environment variables (e.g., <code>MYLLM_API_KEY</code>) to store API keys and other secrets. Do <em>not</em> hardcode them.</li>
<li>Use <code>@EnabledIfEnvironmentVariable</code> to skip the tests if the required environment variables are not set.</li>
<li>Extend <code>AbstractChatModelIT</code>, <code>AbstractStreamingChatModelIT</code>, <code>AbstractEmbeddingModelIT</code>, and/or <code>AbstractScoringModelIT</code> to get a set of basic tests.</li>
<li>Test all relevant features of the model (e.g., text generation, streaming, different parameters, tool use, JSON mode).</li>
<li>Add test for concurrent requests if possible.</li>
<li>Consider adding a test for the <code>Tokenizer</code> interface (see examples in <code>langchain4j-core</code>).</li>
<li>Add <code>@RetryingTest</code> if model response is inconsistent</li>
</ul>
</li>
</ul>
</li>
<li>
<p><strong>Add to BOM (Bill of Materials):</strong> Add your new module to <code>langchain4j-bom/pom.xml</code>. This helps users manage dependencies.</p>
</li>
<li>
<p><strong>Documentation:</strong></p>
<ul>
<li><strong>Update <code>README.md</code>:</strong> Add your integration to the list of supported models and embedding stores.</li>
<li><strong>Create Markdown Documentation:</strong> Create Markdown files in the <code>docs/docs/integrations/</code> directory, following the structure of existing integrations. You&rsquo;ll need:
<ul>
<li>A main file (e.g., <code>my-llm.md</code>).</li>
<li>An entry in <code>docs/docs/integrations/language-models/index.md</code> and in <code>docs/sidebars.js</code>.</li>
<li>An entry in <code>_category_.json</code> files in <code>docs/docs/integrations/language-models</code> and <code>docs/docs/integrations/embedding-stores</code></li>
</ul>
</li>
<li><strong>Examples (Highly Recommended):</strong> Create a simple example in the <a href="https://github.com/langchain4j/langchain4j-examples"><code>langchain4j-examples</code></a> repository. This is <em>very</em> helpful for users.</li>
</ul>
</li>
<li>
<p><strong>General Guidelines (from CONTRIBUTING.md):</strong></p>
<ul>
<li><strong>Java 17:</strong> Maintain compatibility with Java 17.</li>
<li><strong>Minimal Dependencies:</strong> Avoid adding new dependencies if possible. If necessary, try to use libraries already present. Run <code>mvn dependency:analyze</code> to check.</li>
<li><strong>Backwards Compatibility:</strong> Avoid breaking changes. If necessary, deprecate old methods/fields instead of removing them.</li>
<li><strong>Naming Conventions:</strong> Follow existing naming conventions.</li>
<li><strong>No Lombok:</strong> Avoid using Lombok in new code; remove it from existing code if you touch it.</li>
<li><strong>Javadoc:</strong> Add Javadoc where needed.</li>
<li><strong>Code Style:</strong> Run <code>make lint</code> and <code>make format</code> before committing.</li>
<li><strong>Large Features:</strong> Discuss large features with maintainers (@langchain4j) before implementation.</li>
</ul>
</li>
<li>
<p><strong>Open a Pull Request (Draft First):</strong></p>
<ul>
<li>Open a draft PR in the <code>langchain4j-community</code> repository.</li>
<li>Fill out all sections of the PR template.</li>
<li>Once the PR is reviewed and approved, you will be asked to finalize it (add documentation, examples, etc.).</li>
</ul>
</li>
</ol>
<p><strong>Example Code Snippets (Illustrative)</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// MyLlmChatModel.java (in the appropriate package)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">MyLlmChatModel</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">implements</span><span style="color:#bbb"> </span>ChatLanguageModel<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>MyLlmClient<span style="color:#bbb"> </span>client;<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// Your custom client</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>modelName;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Builder</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#06287e">MyLlmChatModel</span>(String<span style="color:#bbb"> </span>baseUrl,<span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>apiKey,<span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>modelName,<span style="color:#bbb"> </span>Duration<span style="color:#bbb"> </span>timeout)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">this</span>.<span style="color:#4070a0">client</span><span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>MyLlmClient(baseUrl,<span style="color:#bbb"> </span>apiKey,<span style="color:#bbb"> </span>timeout);<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// Your custom client</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">this</span>.<span style="color:#4070a0">modelName</span><span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>modelName;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span><span style="color:#06287e">generate</span>(List<span style="color:#666">&lt;</span>ChatMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>messages)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// 1. Convert Langchain4j messages to MyLLM&#39;s request format</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// 2. Call client.chat(...)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// 3. Convert MyLLM&#39;s response to LangChain4j&#39;s Response&lt;AiMessage&gt;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// 4. Handle errors (throw HttpException)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>;<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// TODO: Implement</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ... other methods, builder, etc. ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// MyLlmClient.java (in a suitable package, e.g., dev.langchain4j.model.myllm.internal)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">MyLlmClient</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ... implementation using java.net.http.HttpClient ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// MyLlmChatModelBuilderFactory.java (in a suitable package, e.g., dev.langchain4j.model.myllm.spi)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">MyLlmChatModelBuilderFactory</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">implements</span><span style="color:#bbb"> </span>Supplier<span style="color:#666">&lt;</span>MyLlmChatModel.<span style="color:#4070a0">Builder</span><span style="color:#666">&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>MyLlmChatModel.<span style="color:#4070a0">Builder</span><span style="color:#bbb"> </span><span style="color:#06287e">get</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>MyLlmChatModel.<span style="color:#4070a0">builder</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// META-INF/services/dev.langchain4j.model.chat.spi.ChatLanguageModelBuilderFactory (resource file)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Add a line with the fully qualified name of your factory:</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// dev.langchain4j.model.myllm.spi.MyLlmChatModelBuilderFactory</span><span style="color:#bbb">
</span></span></span></code></pre></div><p><strong>Key Points and Common Mistakes</strong></p>
<ul>
<li><strong>Thorough Testing:</strong> Integration tests are absolutely essential. Test with real API calls.</li>
<li><strong>Error Handling:</strong> Handle HTTP errors (non-2xx responses) from the LLM provider&rsquo;s API.</li>
<li><strong>Token Usage:</strong> If the LLM provider gives you token usage information, include it in the <code>Response</code>.</li>
<li><strong>Finish Reason:</strong> If the LLM provider gives you a finish reason (e.g., &ldquo;stop&rdquo;, &ldquo;length&rdquo;), include it in the <code>Response</code>.</li>
<li><strong>Consistency:</strong> Maintain consistency with existing LangChain4j modules in terms of naming, structure, and coding style.</li>
<li><strong>Documentation:</strong> Clear, concise, and accurate documentation is critical. Follow the patterns you see in existing modules.</li>
<li><strong>SPI Registration:</strong> Remember to create the <code>src/main/resources/META-INF/services/</code> files to register your model implementation as a service.</li>
</ul>
<p>This comprehensive guide, combined with the provided file structure and examples, should give you a strong foundation for contributing your new language model integration to LangChain4j. Remember to start in the <code>langchain4j-community</code> repository for initial development and review. Good luck!</p>
</details>

<h2 id="the-verdict">The verdict</h2>
<p>Honestly, this is really good! It follows the advice in the <a href="https://github.com/langchain4j/langchain4j/blob/main/CONTRIBUTING.md">contributing guidelines</a>.
But since it has access to the whole code base, it understands pretty well the various steps to follow, the classes to implement, etc.</p>
<p>A few nits though: for example, it confirms we shouldn&rsquo;t use Lombok for creating builders, but the example code shows a <code>@Builder</code> annotation from Lombok, because it&rsquo;s still used in several modules. Or there&rsquo;s this reference to the <code>make</code> tool, although the project doesn&rsquo;t use it at all. The code suggests implement the <code>generate()</code> methods, but newer models should now implement the new <code>chat()</code> method instead, but Gemini can&rsquo;t really know about this, as this is fairly new and not many models implement those interfaces for now.</p>
<p>But other than that, it&rsquo;s excellent!</p>
<p>Gemini created a contributing guide by looking at almost 2 million tokens of code and documentation.
But this doesn&rsquo;t end here, you can continue asking other questions, to get more insight into particular aspects of the development of a new module, and Gemini would continue the discussion, guiding you and helping you in your task. I found that quite impressive.</p>
<h2 id="lets-continue-the-conversation-with-the-code-base-and-documentation">Let&rsquo;s continue the conversation with the code base and documentation</h2>
<p>As I was interested in creating a new embedding store for Firestore, I then added in the context the <a href="https://cloud.google.com/firestore/docs/vector-search">documentation</a> of the Java SDK for Firestore. And asked the following question:</p>
<blockquote>
<p>I would also like to create a new embedding store for the Google Cloud Firestore database.
What are the steps to create a new LangChain4j embedding store module?</p></blockquote>
<p>Let&rsquo;s see what it suggested:</p>

<details>
  <summary>Click to reveal the detailed answer</summary>
  <p>Okay, here&rsquo;s a breakdown of how to create a new LangChain4j embedding store module for Google Cloud Firestore, along with the key steps and considerations, mirroring the structure of existing modules like <code>langchain4j-milvus</code>.</p>
<p><strong>Project Structure</strong></p>
<p>Your project structure should follow the established pattern. I&rsquo;ll create a simplified version based on the most relevant parts from the provided file listing. The full structure would be much larger (like the main <code>langchain4j</code> project), but this captures the essentials:</p>
<pre tabindex="0"><code>langchain4j/
└── langchain4j-embedding-store-google-firestore/  (or similar name)
    ├── pom.xml                                     (Your module&#39;s Maven build file)
    └── src/
        └── main/
            └── java/
                └── dev/
                    └── langchain4j/
                        └── store/
                            └── embedding/
                                └── google/
                                    └── firestore/
                                        └── GoogleFirestoreEmbeddingStore.java  (Main class)
                                        └── (Any other supporting classes, like request/response objects)
        └── test/
            └── java/
                └── dev/
                    └── langchain4j/
                        └── store/
                            └── embedding/
                                └── google/
                                    └── firestore/
                                        └── GoogleFirestoreEmbeddingStoreIT.java (Integration test)
</code></pre><p><strong>Steps to Create the Module</strong></p>
<ol>
<li>
<p><strong>Project Setup (Maven Module):</strong></p>
<ul>
<li>Create a new directory named <code>langchain4j-embedding-store-google-firestore</code> (or a similar, descriptive name) within the <code>langchain4j</code> parent directory.</li>
<li>Create a <code>pom.xml</code> file inside this new directory. This file will define your module as a Maven project and specify its dependencies. Use <code>langchain4j-milvus/pom.xml</code> as a guide, but adapt it:
<ul>
<li><strong>Parent:</strong> Set the parent to <code>langchain4j-parent</code>, as shown in your example.</li>
<li><strong>ArtifactId:</strong> Use <code>langchain4j-embedding-store-google-firestore</code>.</li>
<li><strong>Name and Description:</strong> Update these appropriately.</li>
<li><strong>Dependencies:</strong> Include <code>langchain4j-core</code> as a compile-time dependency. Add the Google Cloud Firestore Java client library as a dependency:
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>com.google.cloud<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>google-cloud-firestore<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>YOUR_VERSION_HERE<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span> <span style="color:#60a0b0;font-style:italic">&lt;!-- Lookup the latest version --&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div>Add any other necessary dependencies, such as logging (SLF4J), Lombok (if desired, but try to avoid in new code), etc. <em>Minimize dependencies as much as possible.</em></li>
<li><strong>Test Dependencies:</strong> Include JUnit, AssertJ, and any mocking libraries (like Mockito) in the <code>test</code> scope.</li>
<li><strong>Licenses:</strong> Ensure your module has the correct Apache 2.0 license information in the POM.</li>
</ul>
</li>
</ul>
</li>
<li>
<p><strong>Implement <code>EmbeddingStore&lt;TextSegment&gt;</code>:</strong></p>
<ul>
<li>Create <code>GoogleFirestoreEmbeddingStore.java</code> in the <code>dev.langchain4j.store.embedding.google.firestore</code> package.</li>
<li>Implement the <code>EmbeddingStore&lt;TextSegment&gt;</code> interface from <code>langchain4j-core</code>. This is the <em>crucial</em> part. You&rsquo;ll need to implement the following methods, mapping them to Firestore operations:
<ul>
<li><code>add(Embedding embedding)</code>: Adds a single embedding to the store, generating a unique ID.</li>
<li><code>add(String id, Embedding embedding)</code>: Adds an embedding with a specified ID.</li>
<li><code>add(Embedding embedding, TextSegment textSegment)</code>: Adds an embedding with associated text and metadata.</li>
<li><code>addAll(List&lt;Embedding&gt; embeddings)</code>: Adds multiple embeddings.</li>
<li><code>addAll(List&lt;String&gt; ids, List&lt;Embedding&gt; embeddings, List&lt;TextSegment&gt; textSegments)</code>: Adds multiple embeddings with associated IDs and text segments.</li>
<li><code>removeAll(Collection&lt;String&gt; ids)</code>: Delete embeddings by id</li>
<li><code>removeAll()</code>: Deletes everything</li>
<li><code>removeAll(Filter filter)</code>: Deletes records using the condition filter</li>
<li><code>search(EmbeddingSearchRequest request)</code>: Find related embeddings</li>
<li><code>findRelevant(Embedding referenceEmbedding, int maxResults, double minScore)</code>: Finds relevant embeddings.</li>
</ul>
</li>
<li><strong>Considerations for Firestore Implementation:</strong>
<ul>
<li><strong>Data Model:</strong> How will you store the embeddings (as float arrays) and associated data (text, metadata)? Firestore uses a NoSQL document model. You&rsquo;ll likely store each <code>TextSegment</code> and its <code>Embedding</code> as a document in a collection.</li>
<li><strong>Metadata:</strong> Firestore supports storing metadata as document fields. You&rsquo;ll need a way to map <code>TextSegment</code> metadata (which is a <code>Map&lt;String, String&gt;</code>) to Firestore document fields. You have a few options here, mirroring what existing modules do:
<ul>
<li><strong>Individual Columns (Preferred):</strong> Each metadata key becomes a separate field in the document. This is efficient for querying but requires knowing the metadata keys in advance. The Mariadb integration uses this approach with <code>MetadataColumDefinition</code>.</li>
<li><strong>Single JSON Field:</strong> Store all metadata as a single JSON string. This is flexible but less efficient for filtering. The Chroma integration uses this.</li>
<li><strong>Mixed Approach:</strong> Common metadata fields (like &ldquo;source&rdquo; or &ldquo;document_id&rdquo;) could be separate fields, and a catch-all &ldquo;metadata&rdquo; field could store the rest as JSON.</li>
</ul>
</li>
<li><strong>Vector Search:</strong> Firestore now has native Vector Search, which is excellent! You&rsquo;ll use this for the <code>findRelevant</code> method. The key will be understanding how to map LangChain4j&rsquo;s <code>EmbeddingSearchRequest</code> (which includes <code>maxResults</code> and <code>minScore</code>) to the appropriate Firestore vector search query.</li>
<li><strong>Filtering:</strong> Implement filtering based on metadata using Firestore&rsquo;s query capabilities. LangChain4j&rsquo;s <code>Filter</code> interface needs to be translated into a Firestore query. Look at existing implementations like <code>ChromaMetadataFilterMapper</code> for inspiration, but adapt it to Firestore&rsquo;s query syntax.</li>
<li><strong>Error Handling:</strong> Wrap Firestore exceptions in <code>RuntimeException</code> or a more specific custom exception.</li>
<li><strong>Configuration:</strong> Use a <code>Builder</code> pattern (like <code>OpenAiChatModel</code> does) to allow users to configure:
<ul>
<li>Firestore project ID, database, collection name</li>
<li>Credentials (API key, service account)</li>
<li>Timeout settings</li>
<li>Maximum number of results (<code>maxResults</code>)</li>
<li>Minimum relevance score (<code>minScore</code>)</li>
<li>Field names for text, embedding, and metadata (if configurable)</li>
<li>Possibly options for automatically creating the collection/indexes.</li>
</ul>
</li>
<li><strong>Indexing:</strong> You <em>must</em> create the necessary index in Firestore for vector search to work. The documentation you provided explains how to do this with the <code>gcloud</code> CLI. The code should, ideally, at least <em>check</em> if the index exists. It <em>could</em> even attempt to create it, but this would require more permissions.</li>
<li><strong>Concurrency:</strong> if there is any concurrency, the code should be thread-safe.</li>
</ul>
</li>
</ul>
</li>
</ol>
<ul>
<li><strong>Implement <code>GoogleFirestoreEmbeddingModel</code>:</strong> You should also implement the <code>EmbeddingModel</code> interface, by adapting the google API to the langchain4j interface.</li>
</ul>
<ol start="3">
<li>
<p><strong>Create SPI Builder Factory:</strong></p>
<ul>
<li>Create a <code>GoogleFirestoreEmbeddingStoreBuilderFactory</code> class that implements <code>Supplier&lt;GoogleFirestoreEmbeddingStore.Builder&gt;</code>.</li>
<li>Create a file named <code>META-INF/services/dev.langchain4j.spi.store.embedding.EmbeddingStoreFactory</code> in <code>src/main/resources</code>.</li>
<li>Add a single line to this file containing the fully qualified name of your factory class (e.g., <code>dev.langchain4j.store.embedding.google.firestore.GoogleFirestoreEmbeddingStoreBuilderFactory</code>).</li>
</ul>
</li>
<li>
<p><strong>Write Integration Tests:</strong></p>
<ul>
<li>Create <code>GoogleFirestoreEmbeddingStoreIT.java</code> (and potentially <code>...WithFilteringIT.java</code>, <code>...WithRemovalIT.java</code>, etc., mirroring the existing modules).</li>
<li>Extend <code>EmbeddingStoreIT</code> (or <code>EmbeddingStoreWithFilteringIT</code>, etc.) to inherit a basic set of tests.</li>
<li>Implement the abstract methods (like <code>embeddingStore()</code>) to provide instances of your store and a compatible embedding model.</li>
<li>Add tests specific to Google Cloud Firestore features and limitations.</li>
<li>Use <code>@EnabledIfEnvironmentVariable</code> to conditionally run the tests only when the necessary environment variables (credentials) are set. See <code>OllamaChatModelIT</code> for an example.</li>
</ul>
</li>
<li>
<p><strong>Add to BOM (Bill of Materials):</strong> Add your new module to <code>langchain4j-bom/pom.xml</code> to manage its version consistently.</p>
</li>
<li>
<p><strong>Documentation:</strong></p>
<ul>
<li>Add the new model in all the <code>index.md</code> files</li>
<li>Write a guide in <code>docs/docs/integrations/embedding-stores</code></li>
<li>Create a README file</li>
</ul>
</li>
</ol>
<p><strong>Code Example (Conceptual)</strong></p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// In your GoogleFirestoreEmbeddingStore.java</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">package</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.store.embedding.google.firestore</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.data.embedding.Embedding</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.data.segment.TextSegment</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.model.embedding.EmbeddingModel</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.store.embedding.EmbeddingMatch</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.store.embedding.EmbeddingStore</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">lombok.Builder</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">org.slf4j.Logger</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">org.slf4j.LoggerFactory</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.List</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// ... other imports ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">GoogleFirestoreEmbeddingStore</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">implements</span><span style="color:#bbb"> </span>EmbeddingStore<span style="color:#666">&lt;</span>TextSegment<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>Logger<span style="color:#bbb"> </span>log<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>LoggerFactory.<span style="color:#4070a0">getLogger</span>(GoogleFirestoreEmbeddingStore.<span style="color:#4070a0">class</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ... (fields for Firestore client, collection name, etc.) ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Builder</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#06287e">GoogleFirestoreEmbeddingStore</span>(...)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// ... (initialize Firestore client, etc.) ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">add</span>(Embedding<span style="color:#bbb"> </span>embedding)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// ... implementation ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">add</span>(String<span style="color:#bbb"> </span>id,<span style="color:#bbb"> </span>Embedding<span style="color:#bbb"> </span>embedding)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// ... implementation ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">add</span>(Embedding<span style="color:#bbb"> </span>embedding,<span style="color:#bbb"> </span>TextSegment<span style="color:#bbb"> </span>textSegment)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// ... implementation ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>List<span style="color:#666">&lt;</span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span><span style="color:#06287e">addAll</span>(List<span style="color:#666">&lt;</span>Embedding<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>embeddings)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// ... implementation ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">addAll</span>(List<span style="color:#666">&lt;</span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>ids,<span style="color:#bbb"> </span>List<span style="color:#666">&lt;</span>Embedding<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>embeddings,<span style="color:#bbb"> </span>List<span style="color:#666">&lt;</span>TextSegment<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>embedded)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// ... implementation ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>List<span style="color:#666">&lt;</span>EmbeddingMatch<span style="color:#666">&lt;</span>TextSegment<span style="color:#666">&gt;&gt;</span><span style="color:#bbb"> </span><span style="color:#06287e">findRelevant</span>(Embedding<span style="color:#bbb"> </span>referenceEmbedding,<span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>maxResults,<span style="color:#bbb"> </span><span style="color:#902000">double</span><span style="color:#bbb"> </span>minScore)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// ... implementation using Firestore vector search ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// ... map Firestore results to EmbeddingMatch objects ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ... other methods ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// In a separate file: GoogleFirestoreEmbeddingStoreBuilderFactory.java</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">package</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.store.embedding.google.firestore</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.model.embedding.EmbeddingModel</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.spi.store.embedding.EmbeddingStoreFactory</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">GoogleFirestoreEmbeddingStoreBuilderFactory</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">implements</span><span style="color:#bbb"> </span>EmbeddingStoreFactory<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>GoogleFirestoreEmbeddingStore.<span style="color:#4070a0">Builder</span><span style="color:#bbb"> </span><span style="color:#06287e">get</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">       </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>GoogleFirestoreEmbeddingStore.<span style="color:#4070a0">builder</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p><strong>Key Improvements and Considerations from the examples:</strong></p>
<ul>
<li><strong>Error Handling:</strong> Wrap Firestore API calls in <code>try-catch</code> blocks and throw <code>RuntimeException</code> (or a more specific custom exception) to handle potential errors.</li>
<li><strong>Logging:</strong> Use SLF4J (<code>log.debug()</code>, <code>log.info()</code>, <code>log.warn()</code>, <code>log.error()</code>) for logging. This is crucial for debugging and monitoring.</li>
<li><strong>Configuration:</strong> The <code>Builder</code> should allow configuration of all necessary parameters (endpoint, credentials, collection name, timeouts, etc.). Use <code>ensureNotNull</code> and <code>ensureNotBlank</code> to validate.</li>
<li><strong>Dependency Management:</strong> The <code>pom.xml</code> should correctly specify the Google Cloud Firestore client library as a dependency.</li>
<li><strong>Testcontainers:</strong> Consider using Testcontainers for integration tests to spin up a Firestore emulator. This will make your tests more reliable and portable. (See examples in the provided code, such as for <code>CassandraEmbeddingStoreDockerIT</code>).</li>
<li><strong>Metadata:</strong> Implement proper handling of metadata, including mapping it to/from Firestore document fields. Decide on your strategy (separate fields vs. JSON).</li>
<li><strong>Filtering:</strong> Implement filtering based on metadata using Firestore&rsquo;s query capabilities. You will likely need a helper class like <code>ChromaMetadataFilterMapper</code> to translate <code>Filter</code> objects into Firestore queries.</li>
<li><strong>Service Provider Interface (SPI):</strong> The <code>spi</code> package and <code>META-INF/services</code> file are <em>critical</em> for LangChain4j to discover your implementation. This is how <code>AiServices</code> (and other parts of the framework) dynamically load components. Without this, your module won&rsquo;t be usable.</li>
<li><strong>Return type</strong>: The <code>findRelevant</code> method should take a {@link dev.langchain4j.store.embedding.EmbeddingSearchRequest} and return a {@link dev.langchain4j.store.embedding.EmbeddingSearchResult}. This would ensure that we have consistency through all embedding stores and allow advanced filtering and reranking features.</li>
<li><strong>Interface segregation</strong>: Introduce a <code>EmbeddingStoreWithFiltering</code> interface, that extends <code>EmbeddingStore</code> and adds a <code>removeAll(Filter filter)</code> method.</li>
<li><strong>Metadata handling:</strong> Implement the <code>MetadataHandler</code> interface, or extend one of its base classes if it&rsquo;s a good fit.</li>
<li><strong>Support Vector Search API in different database modes:</strong> Support vector search in the different modes of Cosmos DB, implementing the search in each of them.</li>
<li><strong>Implement the <code>close()</code> method:</strong> You should close the connection to Firestore in the <code>close()</code> method.</li>
<li><strong>Test your embedding store against the <code>EmbeddingStoreWithFilteringIT</code> and <code>EmbeddingStoreWithRemovalIT</code>:</strong> The provided <code>langchain4j-core</code> contains those abstract test classes that you can use to test your embedding store.</li>
<li><strong>Add documentation:</strong> Create Markdown files in the <code>docs/docs/integrations/embedding-stores</code> directory, following the structure of existing integrations.</li>
<li><strong>Add an example:</strong> to the <a href="https://github.com/langchain4j/langchain4j-examples">examples repository</a>.</li>
</ul>
<p>By following these steps and adapting the provided example code, you can create a robust and well-integrated
LangChain4j module for Google Cloud Firestore.</p>
</details>

<h2 id="second-verdict">Second verdict</h2>
<p><strong>Again, it&rsquo;s pretty spot on and acurate!</strong>
Still a mention to Lombok, or a strange reference to Cosmos DB, but I generally like how it advises me to look at existing modules, telling me that this particular one may be a good inspiration to get my implementation right.</p>
<p>I&rsquo;ve never implemented an embedding store so far, and I think newer models should implement a <code>search()</code> method instead of <code>findRelevant()</code>.
So I asked how to implement this method, using the documentation of the Firestore Java SDK, and a minute later, it suggested a concrete implementation.
I won&rsquo;t copy the output again, as I don&rsquo;t want to make this article too long (it already is).
I haven&rsquo;t tried this implementation, but the code and explanations seemed pretty convincing, so when I get some time, I&rsquo;ll try to see if it&rsquo;s correct.</p>
<h2 id="conclusion">Conclusion</h2>
<p><strong>Only Gemini proposes a 2 million token context window</strong>. And I&rsquo;m glad it does, because the LangChain4j projects is close to that limit!
With models with smaller windows, I would have had to be way more selective, and send in the prompt just the right types of artifacts (ie. just the LLM modules, or just the embedding store implementations). Thanks to the huge window, I was able to feed the whole repository in its entirety!</p>
<p><strong>Does it mean it&rsquo;s the end to writing proper tutorials or how-to guides? Certainly not.</strong>
But I find that very interesting that I&rsquo;m able to have this kind of highly detailed conversation with the LLM, without having to understand all the tiny little details of the underlying project, as the model is able to grok it for me, and distills just the right level of information for me to do the task I asked about.</p>
<p>What&rsquo;s very interesting is that <strong>I can continue the conversation</strong> to go in various directions, or <strong>zoom on some specific aspects</strong>, which may not necessarily be covered by existing tutorials or guides. <strong>It&rsquo;s as if I was pair programming with the founder of the project.</strong></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A Generative AI Agent with a real declarative workflow</title><link>https://glaforge.dev/posts/2025/01/31/a-genai-agent-with-a-real-workflow/</link><pubDate>Fri, 31 Jan 2025 13:06:26 +0100</pubDate><guid>https://glaforge.dev/posts/2025/01/31/a-genai-agent-with-a-real-workflow/</guid><description>&lt;p>In my &lt;a href="https://glaforge.dev/posts/2025/01/27/an-ai-agent-to-generate-short-scifi-stories/">previous article&lt;/a>, I detailed how to build an AI-powered short story generation &lt;strong>agent&lt;/strong> using Java, &lt;a href="https://docs.langchain4j.dev/">LangChain4j&lt;/a>, Gemini, and Imagen 3, deployed on Cloud Run jobs.&lt;/p>
&lt;p>This approach involved writing &lt;strong>explicit&lt;/strong> Java code to orchestrate the entire workflow, defining each step programmatically. This follow-up article explores an alternative, &lt;strong>declarative&lt;/strong> approach using &lt;a href="https://cloud.google.com/workflows">Google Cloud Workflows&lt;/a>.&lt;/p>
&lt;p>I&amp;rsquo;ve &lt;a href="https://glaforge.dev/tags/workflows/">written extensively on Workflows&lt;/a> in the past, so for those AI agents that exhibit a very explicit plan and orchestration, I believe Workflows is also a great approach for such declarative AI agents.&lt;/p></description><content:encoded>
<![CDATA[<p>In my <a href="https://glaforge.dev/posts/2025/01/27/an-ai-agent-to-generate-short-scifi-stories/">previous article</a>, I detailed how to build an AI-powered short story generation <strong>agent</strong> using Java, <a href="https://docs.langchain4j.dev/">LangChain4j</a>, Gemini, and Imagen 3, deployed on Cloud Run jobs.</p>
<p>This approach involved writing <strong>explicit</strong> Java code to orchestrate the entire workflow, defining each step programmatically. This follow-up article explores an alternative, <strong>declarative</strong> approach using <a href="https://cloud.google.com/workflows">Google Cloud Workflows</a>.</p>
<p>I&rsquo;ve <a href="https://glaforge.dev/tags/workflows/">written extensively on Workflows</a> in the past, so for those AI agents that exhibit a very explicit plan and orchestration, I believe Workflows is also a great approach for such declarative AI agents.</p>
<h2 id="from-imperative-to-declarative-defining-the-workflow">From imperative to declarative: defining the workflow</h2>
<p>The Java-based agent employed an imperative style, where the code explicitly defined the sequence of operations. Each step, from story conception to image selection, was a method call within the <code>ExplicitStoryGeneratorAgent</code> class (you can check the <a href="https://github.com/glaforge/short-genai-stories/blob/main/fictionStoryAgent/src/main/java/storygen/ExplicitStoryGeneratorAgent.java">code of this class</a>). This provided fine-grained control and allowed for parallelization.</p>
<p><a href="https://cloud.google.com/workflows">Cloud Workflows</a> offers a declarative approach. Instead of writing code, you define the workflow in a YAML file. This file specifies the steps, their inputs and outputs, and the order in which they should be executed. You can also easily create loops (sequential or parallel), and you can implement <em>human in the loop</em> callbacks if needed. The workflow engine then interprets this definition and executes the steps accordingly.</p>
<blockquote>
<p>YAML can be a fair bit more cumbersome to write, compared to using a programming language, but non-developers could get a workflow definition rolling, without having to fire an IDE to code. But for a Java developer, it&rsquo;s certainly simpler to write code, with the help of their favorite programming environment.</p></blockquote>
<p>In this article, I&rsquo;ve created a simplified variant: I removed the <em>LLM-as-judge</em> step that picked the best images. And I&rsquo;ve created pictures for the whole story, not for each chapter. So it&rsquo;s not exactly the same agent workflow as in the preivous article. But I don&rsquo;t want you to drown in too much YAML!</p>
<p>The workflow:</p>
<ul>
<li>creates the story with <strong>Gemini 2</strong>,</li>
<li>creates a prompt (for the whole story, not for each chapter),</li>
<li>generates images with <strong>Imagen 3</strong>,</li>
<li>saves the result in <a href="https://cloud.google.com/firestore">Cloud Firestore</a></li>
</ul>
<p>Let&rsquo;s have a look at the full YAML definition, and read the comments explaining what each step does:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">main</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">params</span>:<span style="color:#bbb"> </span>[input]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic"># Let&#39;s define the Gemini and Image models we want to use:</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">setup</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">GEMINI_MODEL</span>:<span style="color:#bbb"> </span>${&#34;projects/&#34; + sys.get_env(&#34;GOOGLE_CLOUD_PROJECT_ID&#34;) +<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;/locations/us-central1/publishers/google/models/gemini-2.0-flash-exp&#34;</span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">IMAGEN_MODEL</span>:<span style="color:#bbb"> </span>${&#34;projects/&#34; + sys.get_env(&#34;GOOGLE_CLOUD_PROJECT_ID&#34;) +<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;/locations/us-central1/publishers/google/models/imagen-3.0-generate-002&#34;</span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic"># We call Gemini to generate the story</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">generate_story</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>googleapis.aiplatform.v1.projects.locations.endpoints.generateContent<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">model</span>:<span style="color:#bbb"> </span>${GEMINI_MODEL}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">region</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#39;us-central1&#39;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">body</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">contents</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">role</span>:<span style="color:#bbb"> </span>user<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">parts</span>:<span style="color:#bbb">  </span><span style="color:#60a0b0;font-style:italic"># Let&#39;s write a sci-fi story!</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span>- <span style="color:#062873;font-weight:bold">text</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Write a short science-fiction story&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">generationConfig</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">temperature</span>:<span style="color:#bbb"> </span><span style="color:#40a070">2.0</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">responseMimeType</span>:<span style="color:#bbb"> </span>application/json<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#60a0b0;font-style:italic"># Use a JSON schema to define the format of the output</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">responseSchema</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span>OBJECT<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span><span style="color:#062873;font-weight:bold">properties</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">title</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                  </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span>STRING<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                  </span><span style="color:#062873;font-weight:bold">description</span>:<span style="color:#bbb"> </span>The title of the short story<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">content</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                  </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span>STRING<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                  </span><span style="color:#062873;font-weight:bold">description</span>:<span style="color:#bbb"> </span>The body of the story<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span><span style="color:#062873;font-weight:bold">required</span>:<span style="color:#bbb"> </span>[<span style="color:#4070a0">&#39;title&#39;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#39;content&#39;</span>]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#60a0b0;font-style:italic"># You can define system instructions</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">systemInstruction</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">parts</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span>- <span style="color:#062873;font-weight:bold">text</span>:<span style="color:#bbb"> </span>&gt;<span style="color:#4070a0;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">                  You are a creative fiction author,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">                  and your role is to write stories.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">                  You write a story as requested by the user.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">                  A story always has a title,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">                  and is made of 5 long chapters.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">                  Each chapter has a title, is split into paragraphs,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">                  and is at least 20 sentences long.</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>short_story<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">     </span><span style="color:#60a0b0;font-style:italic"># Assign the story, title, content into some variables</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">get_story</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">story_output</span>:<span style="color:#bbb"> </span>${json.decode(short_story.candidates[0].content.parts[0].text)}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">title</span>:<span style="color:#bbb"> </span>${story_output.title}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">content</span>:<span style="color:#bbb"> </span>${story_output.content}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic"># Let&#39;s call Gemini again, but for creating a prompt for Imagen</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">generate_image_prompt</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>googleapis.aiplatform.v1.projects.locations.endpoints.generateContent<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">model</span>:<span style="color:#bbb"> </span>${GEMINI_MODEL}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">region</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#39;us-central1&#39;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">body</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">contents</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">role</span>:<span style="color:#bbb"> </span>user<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">parts</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span>- <span style="color:#062873;font-weight:bold">text</span>:<span style="color:#bbb"> </span>${content}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">systemInstruction</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">parts</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>- <span style="color:#062873;font-weight:bold">text</span>:<span style="color:#bbb"> </span>|<span style="color:#4070a0;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">                  You are an expert artist who masters crafting great
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">                  prompts for image generation models, to illustrate
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">                  short stories.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">                  When given a short story, reply with a concise
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">                  prompt that could be used to create an illustration
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">                  with the Imagen 3 model.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">                  Don&#39;t use any flags like those used with MidJourney.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">                  Just answer with the short concise text prompt.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">                  Your answer MUST start with &#34;A cartoon of &#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">                  as we want to use cartoon or comics illustrations.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">                  The user gives you the following image prompt
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-style:italic">                  for the chapter to illustrate:</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>image_prompt<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic"># Retrieve the prompt from Gemini&#39;s output</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">assign_prompt</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">prompt</span>:<span style="color:#bbb"> </span>${image_prompt.candidates[0].content.parts[0].text}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic"># Time to generate the images</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">image_generation</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>googleapis.aiplatform.v1.projects.locations.endpoints.predict<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">endpoint</span>:<span style="color:#bbb"> </span>${IMAGEN_MODEL}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">region</span>:<span style="color:#bbb"> </span>us-central1<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">body</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">instances</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>- <span style="color:#062873;font-weight:bold">prompt</span>:<span style="color:#bbb"> </span>${prompt}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">parameters</span>:<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic"># Store images in Google Cloud Storage</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">storageUri</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#39;gs://short-scifi-stories-generated-images&#39;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>images<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic"># Utility step to create the picture data for Firestore</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">prepare_images_uri_list</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">create_empty_list</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>- <span style="color:#062873;font-weight:bold">uris</span>:<span style="color:#bbb"> </span>[]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>- <span style="color:#062873;font-weight:bold">uris_for_firestore</span>:<span style="color:#bbb"> </span>[]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">loop_over_images</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">for</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">value</span>:<span style="color:#bbb"> </span>img_object<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">in</span>:<span style="color:#bbb"> </span>${images.predictions}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span>- <span style="color:#062873;font-weight:bold">append_uri</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                  </span>- <span style="color:#062873;font-weight:bold">uris</span>:<span style="color:#bbb"> </span>${list.concat(uris, img_object.gcsUri)}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                  </span>- <span style="color:#062873;font-weight:bold">stringUriMap</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                      </span><span style="color:#062873;font-weight:bold">stringValue</span>:<span style="color:#bbb"> </span>${img_object.gcsUri}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                  </span>- <span style="color:#062873;font-weight:bold">uris_for_firestore</span>:<span style="color:#bbb"> </span>${list.concat(uris_for_firestore, stringUriMap)}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic"># Let&#39;s prepare the final output to return</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic"># as the result of the workflow execution</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">prepare_result</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">final_result</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">title</span>:<span style="color:#bbb"> </span>${title}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">content</span>:<span style="color:#bbb"> </span>${content}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">prompt</span>:<span style="color:#bbb"> </span>${prompt}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">images</span>:<span style="color:#bbb"> </span>${uris}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">createdAt</span>:<span style="color:#bbb"> </span>${sys.now()}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic"># Finally, let&#39;s save the story in Firestore</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">save_to_firestore</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>googleapis.firestore.v1.projects.databases.documents.createDocument<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">collectionId</span>:<span style="color:#bbb"> </span>short-story<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">parent</span>:<span style="color:#bbb"> </span>${&#34;projects/&#34; + sys.get_env(&#34;GOOGLE_CLOUD_PROJECT_ID&#34;) + &#34;/databases/(default)/documents&#34;}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">query</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">documentId</span>:<span style="color:#bbb"> </span>${uuid.generate()}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">body</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">fields</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">title</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span><span style="color:#062873;font-weight:bold">stringValue</span>:<span style="color:#bbb"> </span>${final_result.title}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">content</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span><span style="color:#062873;font-weight:bold">stringValue</span>:<span style="color:#bbb"> </span>${final_result.content}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">prompt</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span><span style="color:#062873;font-weight:bold">stringValue</span>:<span style="color:#bbb"> </span>${final_result.prompt}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">images</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span><span style="color:#062873;font-weight:bold">arrayValue</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">values</span>:<span style="color:#bbb"> </span>${uris_for_firestore}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">createdAt</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span><span style="color:#062873;font-weight:bold">timestampValue</span>:<span style="color:#bbb"> </span>${time.format(final_result.createdAt,&#34;GMT&#34;)}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic"># Return the data</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">return_output</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb"> </span>${final_result}<span style="color:#bbb">
</span></span></span></code></pre></div><p>This YAML file defines the entire story generation process. It calls the Gemini and Imagen APIs, extracts the necessary information from the responses, and saves the final result to Firestore. No Java code is required to manage the flow of execution.</p>
<h2 id="key-differences-and-trade-offs">Key differences and trade-offs</h2>
<p>Let&rsquo;s zoom in on the pros and cons of both approaches.</p>
<h3 id="imperative--programming-approach">Imperative / programming approach:</h3>
<ul>
<li><strong>Pros</strong>:
<ul>
<li>Fine-grained control over the workflow.</li>
<li>Explicit parallelization for performance optimization.</li>
<li>Familiar programming and debugging tools.</li>
<li>Cloud Run jobs is fully managed and scaled by Google Cloud.</li>
<li>Job execution can be scheduled by Cloud Scheduler.</li>
</ul>
</li>
<li><strong>Cons</strong>:
<ul>
<li>You need to be familiar with a programming language &amp; environment.</li>
<li>It can potentially be challenging to maintain as the workflow evolves.</li>
<li>The approach used required being familiar with running &amp; scheduling containers as jobs.</li>
</ul>
</li>
</ul>
<h3 id="declarative--workflow-based-approach">Declarative / workflow based approach:</h3>
<ul>
<li><strong>Pros</strong>:
<ul>
<li>Pretty easy-to-understand workflow definitions.</li>
<li>Workflows offers a visualisation of the steps (also during execution).</li>
<li>Parallelization can be defined explicitly (with the <code>parallel</code> keyword on iterations or step branches).</li>
<li>Simplified maintenance and updates. Just need to update the YAML in the console.</li>
<li>Workflows is scalable and reliable out of the box without extra effort.</li>
<li>Workflow execution can be scheduled by Cloud Scheduler.</li>
</ul>
</li>
<li><strong>Cons</strong>:
<ul>
<li>YAML authoring can be painful, if you&rsquo;re not familiar with the APIs you call.</li>
<li>Parallelization is declarative but might be limited depending on the workflow definition and Google Cloud Workflows capabilities. You would have more control with a programming language.</li>
<li>There&rsquo;s no emulator to run workflows locally, so you might have to create copies and work on these, to not affect the production workflow.</li>
<li>Debugging relies on workflow execution logs, which might be less intuitive than traditional debugging.</li>
</ul>
</li>
</ul>
<h2 id="choosing-the-right-approach">Choosing the right approach</h2>
<p>It depends! &#x1f605;</p>
<p>Of course, the choice between these approaches depends on the specific project requirements. If fine-grained control and explicit parallelization are critical, the imperative programming approach might be preferable.</p>
<p>However, for simpler workflows where ease of development and maintainability are critical, Cloud Workflows offers an interesting alternative. You can easily make a tweak to the workflow directly from the Google Cloud console if needed.</p>
<p>In the case of this story generation agent, the declarative approach sounds like a good fit, but the YAML authoring can be a bit painful at times, as you have to look up the various payload schemas for the APIs to invoke, to be able to make the service calls. But that&rsquo;s definitely a plus as well, in the sense that pretty much all the products and services offered on Google Cloud Platform can easily be called via REST endpoints, and Workflows excels at that.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Explicit declarative planning helps AI agents stay focused, and ensures a high level of predictability.
My experience with agents which plan their own actions has been mixed, as sometimes the LLM hallucinates function calls, or calls functions with bogus parameters.
In the previous <a href="https://glaforge.dev/posts/2025/01/27/an-ai-agent-to-generate-short-scifi-stories/">previous article</a>, I used an imperative programming approach, but in this article today, I developed a simplified equivalent with a declarative workflow definition.</p>
<p><a href="https://cloud.google.com/workflows">Google Cloud Workflows</a> offers a powerful and convenient way to build and manage declarative AI agents — and obviously any other kind of process that needs to call APIs. By defining the workflow declaratively, you can focus on the logic of your agent rather than the details of execution. While it might not be suitable for every use case, it&rsquo;s definitely a valuable tool to consider when building AI-powered applications on Google Cloud!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>An AI agent to generate short sci-fi stories</title><link>https://glaforge.dev/posts/2025/01/27/an-ai-agent-to-generate-short-scifi-stories/</link><pubDate>Mon, 27 Jan 2025 08:06:40 +0100</pubDate><guid>https://glaforge.dev/posts/2025/01/27/an-ai-agent-to-generate-short-scifi-stories/</guid><description>&lt;p>This project demonstrates how to build a fully automated short story generator using Java, &lt;a href="https://docs.langchain4j.dev/">LangChain4j&lt;/a>, Google Cloud&amp;rsquo;s &lt;strong>Gemini&lt;/strong> and &lt;strong>Imagen 3&lt;/strong> models, and a serverless deployment on &lt;a href="http://cloud.run/">Cloud Run&lt;/a>.&lt;/p>
&lt;p>Every night at midnight UTC, a new story is created, complete with AI-generated illustrations, and published via Firebase Hosting.
So if you want to read a new story every day, head over to:&lt;/p>
&lt;h2 id="-short-ai-storywebapphttpsshort-ai-storywebapp-">→ &lt;a href="https://short-ai-story.web.app/">short-ai-story.web.app&lt;/a> ←&lt;/h2>
&lt;p>The code of this agent is available on Github. So don&amp;rsquo;t hesitate to &lt;strong>check out the code&lt;/strong>:&lt;/p></description><content:encoded>
<![CDATA[<p>This project demonstrates how to build a fully automated short story generator using Java, <a href="https://docs.langchain4j.dev/">LangChain4j</a>, Google Cloud&rsquo;s <strong>Gemini</strong> and <strong>Imagen 3</strong> models, and a serverless deployment on <a href="http://cloud.run/">Cloud Run</a>.</p>
<p>Every night at midnight UTC, a new story is created, complete with AI-generated illustrations, and published via Firebase Hosting.
So if you want to read a new story every day, head over to:</p>
<h2 id="-short-ai-storywebapphttpsshort-ai-storywebapp-">→ <a href="https://short-ai-story.web.app/">short-ai-story.web.app</a> ←</h2>
<p>The code of this agent is available on Github. So don&rsquo;t hesitate to <strong>check out the code</strong>:</p>
<h2 id="-githubcomglaforgeshort-genai-storieshttpsgithubcomglaforgeshort-genai-stories-">→ <a href="https://github.com/glaforge/short-genai-stories">github.com/glaforge/short-genai-stories</a> ←</h2>
<p>Let&rsquo;s have a closer look at the architecture and workflow of this automated storytelling machine.</p>
<h2 id="the-agent-the-storytellers-brain">The agent: the storyteller&rsquo;s brain</h2>
<p>At the heart of the system lies the <code>ExplicitStoryGeneratorAgent</code>, a Java class orchestrating the entire story generation process. This agent follows a clear, multi-step workflow:</p>
<ul>
<li><strong>Story conception (Gemini)</strong>: The agent first calls the Gemini large language model (LLM) to generate the core story elements: a title, five chapters each with title and content.</li>
<li><strong>Image prompt engineering (Gemini)</strong>: For each chapter&rsquo;s content, the agent again leverages Gemini to craft tailored image generation prompts. This ensures that the image prompts are relevant to the specific content of each chapter.</li>
<li><strong>Illustration generation (Imagen 3)</strong>: Using the generated prompts, the agent calls Imagen 3 to produce a set of image candidates (four by default) for each chapter.</li>
<li><strong>Image selection (Gemini, self-reflection)</strong>: In a unique <em>&ldquo;self-reflection&rdquo;</em> step, the agent presents the generated images back to Gemini, asking the LLM to select the image that best visually represents the chapter&rsquo;s narrative. This crucial step ensures that the illustrations truly complement the story.</li>
<li><strong>Persistence (Firestore)</strong>: Once the story, chapter titles, content, and selected images are finalized, the agent stores them in a Firestore database (a NoSQL document database). This makes retrieving complete data relatively straightforward from the web frontend, thanks to the <a href="https://firebase.google.com/">Firebase</a> framework.</li>
</ul>
<p>For the more visual people among us, this diagram illustrates the steps above:</p>
<p><figure>
  <a href="#img-f645caa4943d6835f8238b80e9b91aae">
    <img src="/img/short-ai-stories/agent-workflow.png"
      alt="Agent workflow diagram"
       />
  </a>
  <figcaption>Agent workflow diagram</figcaption>
</figure>
<div class="lightbox" id="img-f645caa4943d6835f8238b80e9b91aae">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/short-ai-stories/agent-workflow.png"
    alt="Agent workflow diagram"
     />
  <div class="lightbox-caption">Agent workflow diagram</div>
</div>
</p>
<p><strong>Note:</strong> The critique step where Gemini is asked to judge the best image isn&rsquo;t really necessary, to be honest. Imagen generates images that adhere very much to the given prompt. So either of them would seem suitable to illustrate each chapter. But it was interesting to implement a <em>self-reflection</em> step in this workflow.</p>
<h2 id="digression-explicit-vs-autonomous-agent-workflows">Digression: Explicit vs. Autonomous Agent Workflows</h2>
<p>This project utilizes an <em>explicit workflow agent</em>, where the story generation process is meticulously defined and controlled by the Java code. This approach contrasts with fully <em>autonomous agents</em>, which rely on the LLM to plan and execute the workflow dynamically.</p>
<p>Let&rsquo;s explore the key differences and trade-offs between these two approaches:</p>
<h3 id="explicit-workflow-agent-code-driven-planning">Explicit workflow agent (code-driven planning):</h3>
<ul>
<li><strong>Predictable execution</strong>: The Java code dictates the exact sequence of steps, ensuring a highly predictable and reliable workflow. Each stage, from story conception to image selection, is explicitly programmed, leaving no room for unexpected deviations.</li>
<li><strong>Improved performance through parallelization</strong>: With explicit control, tasks that can be executed concurrently (such as generating images for different chapters or judging the best image for each chapter) can be easily parallelized. This significantly reduces the overall execution time.</li>
<li><strong>Easier debugging and maintenance</strong>: The clear, structured code makes debugging and maintenance straightforward. The flow of execution is transparent to the developer, and any errors can be readily identified and addressed.</li>
<li><strong>Limited flexibility</strong>: The explicit nature of the workflow could be seen as offering less flexibility. Indeed, the code needs to be updated to handle changes of the workflow. However, it&rsquo;s not necessarily worse than endlessly tweaking prompts to coerce an LLM to plan correctly the needed workflow changes.</li>
</ul>
<h3 id="autonomous-agent-llm-driven-planning">Autonomous agent (LLM-driven planning):</h3>
<ul>
<li><strong>Dynamic workflow</strong>: Autonomous agents use the LLM&rsquo;s capabilities to plan and execute the workflow. This allows for greater flexibility and adaptability to different story generation requirements. The LLM can theoretically decide which steps to take, in which order, and how many times.</li>
<li><strong>Potential for hallucinations and errors</strong>: Relying on the LLM for planning introduces the risk of hallucinations and incorrect function calls. The LLM might generate nonsensical steps, omit crucial actions, provide incorrect parameters to functions, or execute functions in an illogical order. This can lead to unpredictable results and make it harder to catch potential errors. Even with perfect prompts, LLMs might make mistakes in function calling. This is actually the problem I encountered when trying this approach first.</li>
<li><strong>Debugging challenges</strong>: Debugging autonomous agents can be more complex. The dynamic nature of the workflow makes it harder to trace the execution path and identify the source of errors. Troubleshooting often involves analyzing the logs of the LLM and the tools it requested to call, which can be challenging to interpret at times.</li>
<li><strong>Less control over execution</strong>: With autonomous agents, developers cede some control over the execution flow to the LLM. While this offers flexibility, it also means less fine-grained control over performance optimization. Parallelization opportunities, for example, might not be readily apparent or easily exploitable. Currently, when receiving paralell function call requests, LangChain4j doesn&rsquo;t yet offer the possibility to request their paralellization.</li>
</ul>
<p>The autonomous approach would have looked like the following diagram:</p>
<p><figure>
  <a href="#img-d77b5566f0d72b614e9c8e187c39bb59">
    <img src="/img/short-ai-stories/autonomous-approach.png"
      alt="Autonomous agent diagram"
       />
  </a>
  <figcaption>Autonomous agent diagram</figcaption>
</figure>
<div class="lightbox" id="img-d77b5566f0d72b614e9c8e187c39bb59">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/short-ai-stories/autonomous-approach.png"
    alt="Autonomous agent diagram"
     />
  <div class="lightbox-caption">Autonomous agent diagram</div>
</div>
</p>
<p>With this approach, the main agent generates the story, then would call first the prompt creation tool, then the generation image tool, and would finish with the tool to pick up the best image. However, in my experience, in spite of a good amount of prompt engineering tweaks, I couldn&rsquo;t get this to work reliably. I tried with different versions of Gemini (1.5 Flash, 1.5 Pro, and 2.O Flash experimental, from worst to best outcome), but sometimes, for example, it would request to judge images before they had been generated, or the URLs of the images would be hallucinated instead of coming from the outcome of the judge. So I prefered moving to a more explicit approach.</p>
<p>I invite you to read this great article from Anthropic about <a href="https://www.anthropic.com/research/building-effective-agents">building effective agents</a> which also makes the distiction between <em>agents</em> (fully autonomous planning agents) and <em>workflows</em> (the more explicit approach with code driving the execution planning). They also recommend to stick to <em>workflows</em> when the logic of the agent is very clear upfront: when you can draw a workflow on a sheet of paper, that&rsquo;s surely because you do need a workflow.</p>
<h3 id="choosing-the-right-approach">Choosing the right approach:</h3>
<p>The choice between explicit and autonomous workflows depends on the specific requirements of the project. Explicit workflows are best suited for applications where predictability, reliability, and performance are paramount.</p>
<p>Autonomous agents are more appropriate when flexibility, adaptability, and dynamic planning are prioritized, even at the cost of potential errors and increased debugging complexity.</p>
<p>This project prioritizes the former over the latter, which explains why an explicit agent is preferred for this use case. In other words, it&rsquo;s better to have a predictable solution, which then can be easily parallelized to reduce latency, than a non-predictable solution that is slower.</p>
<h2 id="a-closer-look-at-the-code">A closer look at the code</h2>
<p>As you&rsquo;ll be exploring the <a href="https://github.com/glaforge/short-genai-stories">code base</a>, I&rsquo;d like to highlight a few points.</p>
<p>The <code>ExplicitStoryGeneratorAgent</code> class uses a structured and predictable approach to orchestrating the LLM. Its core logic resides within the <code>main()</code> method, outlining a clear, step-by-step workflow:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// 1️⃣ let&#39;s prepare the story</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Story<span style="color:#bbb"> </span>story<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>prepareStory(<span style="color:#4070a0">&#34;a science-fiction novel&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// 2️⃣ iterate over each chapter in parallel</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>List<span style="color:#666">&lt;</span>Story.<span style="color:#4070a0">Chapter</span><span style="color:#666">&gt;</span><span style="color:#bbb"> </span>newChaptersWithImages<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>story.<span style="color:#4070a0">chapters</span>.<span style="color:#4070a0">stream</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">parallel</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">map</span>(chapter<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// 3️⃣ prepare an impage prompt for each chapter</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>imagePrompt<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>prepareImagePromptForChapter(chapter);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// 4️⃣ generate up to 4 images per chapter</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>List<span style="color:#666">&lt;</span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>imagesForChapter<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>generateImages(imagePrompt);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// 5️⃣ judge the best image for this chapter</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>bestImage<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>pickBestImageForChapter(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>chapter.<span style="color:#4070a0">chapterContent</span>,<span style="color:#bbb"> </span>imagesForChapter);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Story.<span style="color:#4070a0">Chapter</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>chapter.<span style="color:#4070a0">chapterTitle</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>chapter.<span style="color:#4070a0">chapterContent</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>bestImage);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}).<span style="color:#4070a0">toList</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Story<span style="color:#bbb"> </span>newStoryWithImages<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Story(story.<span style="color:#4070a0">title</span>,<span style="color:#bbb"> </span>newChaptersWithImages);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// 6️⃣ save the story to Firestore</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>saveToFirestore(newStoryWithImages);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Story generation depends on <em>structured output</em>: The agent uses Gemini to generate the story&rsquo;s title and five chapters, each with a title and content. Crucially, it leverages Java <code>record</code>s and <code>responseSchema</code> to ensure type safety and consistent outputs. You&rsquo;ll notice the use of <code>@Description</code> annotations to ensure the LLM really understands what each field corresponds to:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">Story</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Description</span>(<span style="color:#4070a0">&#34;The title of the story&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>title,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Description</span>(<span style="color:#4070a0">&#34;The chapters of the story&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>List<span style="color:#666">&lt;</span>Chapter<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>chapters)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">Chapter</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@Description</span>(<span style="color:#4070a0">&#34;The title of the chapter&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>chapterTitle,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@Description</span>(<span style="color:#4070a0">&#34;The content of the chapter&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>chapterContent,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@Description</span>(<span style="color:#4070a0">&#34;The Google Cloud Storage URI of the image...&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>gcsURI)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>To configure the model generation to use structured outputs, here&rsquo;s how the schema of this output is defined:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>chatModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(GCP_PROJECT_ID)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(GCP_LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(CHAT_MODEL_NAME)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">temperature</span>(1.<span style="color:#4070a0">5f</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseSchema</span>(Schema.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setType</span>(Type.<span style="color:#4070a0">OBJECT</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">putProperties</span>(<span style="color:#4070a0">&#34;title&#34;</span>,<span style="color:#bbb"> </span>Schema.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">setDescription</span>(<span style="color:#4070a0">&#34;The title of the story&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">setType</span>(Type.<span style="color:#4070a0">STRING</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">putProperties</span>(<span style="color:#4070a0">&#34;chapters&#34;</span>,<span style="color:#bbb"> </span>Schema.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">setDescription</span>(<span style="color:#4070a0">&#34;The list of 5 chapters&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">setType</span>(Type.<span style="color:#4070a0">ARRAY</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">setItems</span>(Schema.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">setDescription</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span><span style="color:#4070a0">&#34;A chapter with a title, and its content&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">setType</span>(Type.<span style="color:#4070a0">OBJECT</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">putProperties</span>(<span style="color:#4070a0">&#34;chapterTitle&#34;</span>,<span style="color:#bbb"> </span>Schema.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">setType</span>(Type.<span style="color:#4070a0">STRING</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">setDescription</span>(<span style="color:#4070a0">&#34;The title of the chapter&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">putProperties</span>(<span style="color:#4070a0">&#34;chapterContent&#34;</span>,<span style="color:#bbb"> </span>Schema.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">setType</span>(Type.<span style="color:#4070a0">STRING</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">setDescription</span>(<span style="color:#4070a0">&#34;The content of the chapter, &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#4070a0">&#34;made of 20 sentences&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">addAllRequired</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>List.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;chapterTitle&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;chapterContent&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">addAllRequired</span>(List.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;title&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;chapters&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>It&rsquo;s possible to simplify the schema creation by taking advantage of a helper class. This schema could have been simplified to:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseSchema</span>(SchemaHelper.<span style="color:#4070a0">fromClass</span>(Story.<span style="color:#4070a0">class</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>To instruct the LLM at each step, I tend to use system instructions for setting the role and goal for the LLM, but I use user messages to give the more variable part, like the chapter&rsquo;s content, or the image prompt. Here&rsquo;s an example:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>chatModel.<span style="color:#4070a0">generate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>SystemMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You are a creative fiction author, and your role is to write stories.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You write a story as requested by the user.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        A story always has a title, and is made of 5 long chapters.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Each chapter has a title, is split into paragraphs, \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        and is at least 20 sentences long.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>UserMessage.<span style="color:#4070a0">from</span>(storyType)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>The <code>storyType</code> variable in the user message contains the type of story to generate, like <code>&quot;a science-fiction story&quot;</code>.
It&rsquo;s currently set in stone, but you could parameterize this to generate fantasy novels, love stories, etc.</p>
<p>The <em>self-reflection</em> step, where the LLM judges which is the best illustration for a chapter is taking advantage of Gemini&rsquo;s multimodal capabilities.
Indeed, Gemini receives the instruction of picking the best image out of a few, and it is given the text of the request (and the URLs of the pictures), as well as inline references to those images (ie. the Google Cloud Storage URI, pointing at the location of the pictures). Thus, this is a multimodal request, as both text and images are passed in the prompt:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>List<span style="color:#666">&lt;</span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>imagesForChapter<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>generateImages(imagePrompt);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>bestImage<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>pickBestImageForChapter(chapter.<span style="color:#4070a0">chapterContent</span>,<span style="color:#bbb"> </span>imagesForChapter);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Inside pickBestImageForChapter we have:</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>List<span style="color:#666">&lt;</span>ChatMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>messages<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ArrayList<span style="color:#666">&lt;&gt;</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>messages.<span style="color:#4070a0">add</span>(SystemMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;...prompt to select best image...&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>messages.<span style="color:#4070a0">add</span>(UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;...chapter content...&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>imagesForChapter.<span style="color:#4070a0">forEach</span>(imageUrl<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// Send each URL as text and as image to the model</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>messages.<span style="color:#4070a0">add</span>(UserMessage.<span style="color:#4070a0">from</span>(imageUrl<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;\n&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>messages.<span style="color:#4070a0">add</span>(UserMessage.<span style="color:#4070a0">from</span>(ImageContent.<span style="color:#4070a0">from</span>(imageUrl)));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>});<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>chatModel.<span style="color:#4070a0">generate</span>(messages);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// ... parse best image from response</span><span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="building-the-application">Building the application</h2>
<p>The project employs a standard Java development workflow using Maven for dependency management and building:</p>
<ul>
<li><strong>Dependencies</strong>: The <code>pom.xml</code> file defines the project&rsquo;s dependencies, including LangChain4j (for LLM orchestration), the Google Cloud Firestore library (for data persistence), and Google Cloud&rsquo;s Gemini and Imagen libraries.</li>
<li><strong>Packaging</strong>: The Maven build process packages the application into a JAR, and its dependencies by its side. I followed the approach explained in that <a href="https://medium.com/holisticon-consultants/dont-build-fat-jars-for-docker-applications-6252a5571248">article</a>: to build a JAR with its dependencies on the side, instead of a <em>shaded / fat</em> JAR. One benefit I see is that the dependencies are one container layer, while the application itself is another, so it should make Docker building layer faster, as the dependencies don&rsquo;t change often, and that dependency layer would be cached.</li>
<li><strong>Containerization (Docker)</strong>: A <code>Dockerfile</code> is used to containerize the application. The container image includes the executable JAR and dependencies, as well as the Java runtime environment. I used Azul&rsquo;s <a href="https://hub.docker.com/layers/azul/zulu-openjdk-distroless/21-latest/images/sha256-986ca559b15738378f4a67a563c117159136c5eff705b91ba68f0fe8883246ef?context=explore">Zulu distroless Java 21</a> base image. The container is finally built thanks to Cloud Build.</li>
</ul>
<h2 id="deployment-and-automation">Deployment and automation</h2>
<p>To automate story generation and deployment, the project leverages several Google Cloud services:</p>
<ul>
<li><strong>Cloud Build</strong>: Cloud Build automates the process of building the Docker container image. The provided <code>justfile</code> contains commands and recipes to build and submit the container image (I covered <code>just</code> in a <a href="https://glaforge.dev/posts/2023/06/07/just-a-handy-command-line-tool/">previous article</a>, a nifty little tool to parameterize and run common commands for the project). I simply followed the tutorial in the Cloud Build documentation to <a href="https://cloud.google.com/build/docs/running-builds/submit-build-via-cli-api">submit a build via the CLI</a> (the <code>gcloud</code> CLI SDK), after having done some IAM setup as explained <a href="https://cloud.google.com/build/docs/build-push-docker-image">here</a> to be able to push the built image in <a href="https://cloud.google.com/artifact-registry/docs">Artifact Registry</a>.</li>
<li><strong>Cloud Run jobs</strong>: The application runs as a Cloud Run job. Contrary to Cloud Run services, where incoming HTTP requests trigger the service, here, jobs are triggered and run to completion. The Cloud Run job allows for serverless execution of the story generation agent. I followed this guide to <a href="https://cloud.google.com/run/docs/create-jobs#gcloud">create jobs</a>. Don&rsquo;t forget to set up the <a href="https://cloud.google.com/run/docs/configuring/jobs/environment-variables">required environment variables</a>.</li>
<li><strong>Cloud Scheduler</strong>: Cloud Scheduler triggers the Cloud Run job every day at midnight UTC. This automation ensures that a new story is generated and published daily. To configure this, this page explains how to set <a href="https://cloud.google.com/run/docs/execute/jobs-on-schedule#command-line">scheduled triggers</a>.</li>
<li><strong>Firebase Hosting</strong>: Firebase Hosting serves the static assets of the website (HTML, CSS, JavaScript) that displays the stories. Firebase also provides easy access to the Firestore database where the stories are stored, at the last stage of our agentic workflow.</li>
</ul>
<h2 id="further-possible-improvements">Further possible improvements</h2>
<p>I&rsquo;m not in the business of selling novels, so I won&rsquo;t really spend much more time improving this application. However, I noticed a few areas where this project could be improved.</p>
<h3 id="more-creativity">More creativity</h3>
<p>When reading the short stories, you&rsquo;ll notice a certain lack of creativity. Somehow, the stories often happen around the years 2340, the action takes places on Xylos, and some characters appear very frequently, like Aris Thorne. Similarly, some words or concepts appear all the time, like the words <em>echoes</em>, <em>obsidian</em>, <em>crimson</em>, etc. Maybe the model has seen such novels, with such personas, locations, time period, in its training. I&rsquo;ve seen online some people getting the <a href="https://www.semanticpen.com/tools/ai-generated-superhero-origin-story-generator/female-superhero-origin-story--e894532b-b775-4fc8-82d4-e0afcb31afad">same kind of stories</a>, and even a <a href="https://www.amazon.com/Veins-Starlight-Forbidden-Astral-Weavers-ebook/dp/B0DPKY5C4R">book</a> with the same characters or location.</p>
<p>I think it&rsquo;d be interesting to explore how to make the stories more diverse and varied. For example by adding more steps in the workflow to work on character creation, on different narrative arcs, on environment definitions. For science-ficiton only, there are tons of <a href="https://www.rachelagreco.com/30-types-of-science-fiction-every-sci-fi-lover-should-know/">types of sci-fi stories</a>. My friend, Philippe Charrière, worked on <a href="https://k33g.hashnode.dev/how-to-generate-random-rpg-character-names-with-an-llm">how to generate random RPG character names with LLMs</a>. He shared plenty of ideas on how to guide LLMs to get more creative with personas.</p>
<h3 id="character-definition-for-illustration-consistency">Character definition for illustration consistency</h3>
<p>Speaking of character creation, if you look at the illustrations, you&rsquo;ll see that the characters often don&rsquo;t have the same style or appearance. Indeed, I don&rsquo;t give Imagen the whole context of the story when I let Gemini create the image prompts. A possible area of improvement could be to work on proper character definitions (face characteristics, attire, etc.), and ensure that the information is passed through to Imagen. The same would apply for the setting, like the planet, the spaceship details, and more.</p>
<h3 id="chapter-legibility">Chapter legibility</h3>
<p>Each story is split into 5 chapters, of about 20 sentences or so. I tried to make Gemini to generate paragraphs, to improve legibility. However, in spite of a bit of time spent on tweaking the prompts, I failed to coerce it to create paragraphs to delineate the key sections of the chapters. When prompting can&rsquo;t solve this, an extra LLM call loop can take the chapter&rsquo;s content and make it more readable.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The key take away of this experimetnation, is that <strong>when you can describe your AI agent&rsquo;s plan of action with an explicit and predictable workflow, you should definitely follow that route, and avoid giving the LLM the freedom to handle the planning alone</strong>. LLM autonomous planning works much better in more unpredictable cases, where steps can&rsquo;t be foreseen. Be sure to use the right approach!</p>
<p>Again, Gemini and Imagen were up to the task for this new project and gave great stories and illustrations, even if the creativity could be improved. And I&rsquo;ll keep using <a href="https://docs.langchain4j.dev/">LangChain4j</a> as my Swiss-army knife for all my Generative AI projects, as it works reliably, and offers rich capabilities.</p>
<p>Knowing that I would build a <em>workflow</em>, I also explored the use of my beloved <a href="https://cloud.google.com/workflows">Google Cloud Workflows</a> which I&rsquo;ve <a href="https://glaforge.dev/tags/workflows/">written a lot about</a>. I&rsquo;ll likely write another (shorter) article where I&rsquo;ll show how to create such GenAI workflows with it, stay tuned.</p>
<p>This project was also a good opportunity for me to use Cloud Run jobs. I love Cloud Run for all my serverless, auto-scaled, fully-managed, HTTP services, but I hadn&rsquo;t used a Cloud Run <em>job</em> so far. For such <em>batch</em> kind of tasks, this is the right tool for the <em>job</em> (pun intended)! There&rsquo;s also <a href="https://cloud.google.com/batch">Google Cloud Batch</a> but it&rsquo;s more for heavier computation kind of workloads.</p>
<p>So what&rsquo;s next? Checkout the website to <a href="https://short-ai-story.web.app/">read a short story every day</a>, and explore the <a href="https://github.com/glaforge/short-genai-stories">code base</a> to better understand how stories are baked. If you want to replicate this application, and haven&rsquo;t yet tried Google Cloud, feel free to use the <a href="https://cloud.google.com/free">$300 of credits for new users</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Analyzing trends and topics from Bluesky's Firehose with generative AI</title><link>https://glaforge.dev/posts/2025/01/06/analyzing-trends-and-topics-from-blueskys-firehose-with-generative-ai/</link><pubDate>Mon, 06 Jan 2025 10:10:13 +0100</pubDate><guid>https://glaforge.dev/posts/2025/01/06/analyzing-trends-and-topics-from-blueskys-firehose-with-generative-ai/</guid><description>&lt;p>First article of the year, so let me start by wishing you all, my dear readers, a very happy new year!
And what is the subject of this new piece of content?
For a while, I&amp;rsquo;ve been interested in analyzing trends and topics in social media streams.
I recently joined Bluesky (you can follow me at &lt;a href="https://bsky.app/profile/glaforge.dev">@glaforge.dev&lt;/a>),
and contrarily to X, it&amp;rsquo;s possible to access its Firehose
(the stream of all the messages sent by its users) pretty easily, and even for free.
So let&amp;rsquo;s see what we can learn from the firehose!&lt;/p></description><content:encoded>
<![CDATA[<p>First article of the year, so let me start by wishing you all, my dear readers, a very happy new year!
And what is the subject of this new piece of content?
For a while, I&rsquo;ve been interested in analyzing trends and topics in social media streams.
I recently joined Bluesky (you can follow me at <a href="https://bsky.app/profile/glaforge.dev">@glaforge.dev</a>),
and contrarily to X, it&rsquo;s possible to access its Firehose
(the stream of all the messages sent by its users) pretty easily, and even for free.
So let&rsquo;s see what we can learn from the firehose!</p>
<p>Without further ado, here&rsquo;s the end goal!</p>
<p><figure>
  <a href="#img-14c84f280b13e11b1ca0b16e2380ffd1">
    <img src="/img/bluesky/bluesky-visualization.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-14c84f280b13e11b1ca0b16e2380ffd1">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bluesky/bluesky-visualization.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h2 id="blueskys-firehose--a-stream-of-social-messages">Bluesky&rsquo;s Firehose — a stream of social messages</h2>
<p>The underlying protocol used by Bluesky is the <a href="https://atproto.com/">AT Protocol</a>.
There&rsquo;s an API to access Bluesky&rsquo;s streams via this protocol, but it&rsquo;s a bit cumbersome to use.
In order to reduce the quantity of data sent via the AT protocol over its &ldquo;relay&rdquo; network,
the Bluesky team introduced <a href="https://docs.bsky.app/blog/jetstream">JetStream</a>,
to relay all the messages as well, via WebSockets, in JSON format, for a fraction of the size of the AT protocol payloads.
You can also read about how they <a href="https://jazco.dev/2024/09/24/jetstream/">shrinked the payloads by 99%</a>!</p>
<p>The <a href="https://github.com/bluesky-social/jetstream">JetStream Github repository</a> shares the endpoints you can use to access the firehose,
and gives some details about the various types of payloads (new messages, likes, shares, etc.)
It also mentioned a nice little tool called <a href="https://github.com/vi/websocat">websocat</a>
which is a command line tool to connect to WebSockets — very handy to analyze the payloads.</p>
<p>To better understand the JSON message formats, I used websocat, as well as Simon Willison&rsquo;s client-side
<a href="https://simonwillison.net/2024/Nov/20/bluesky-websocket-firehose/">online tool</a>
to access the JetStream, and see the flows of messages.</p>
<h2 id="a-birds-eye-view-of-the-project">A bird&rsquo;s eye view of the project</h2>
<p>Before diving into the code, and showing how to fetch the Bluesky posts,
I&rsquo;d like to give you a high level overview of what we&rsquo;re going to implement.</p>
<p><figure>
  <a href="#img-ae730e81d2585a5fafa6042c8733d025">
    <img src="/img/bluesky/bluesky-jetstream-trends.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-ae730e81d2585a5fafa6042c8733d025">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bluesky/bluesky-jetstream-trends.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<blockquote>
<p>I used <a href="https://www.napkin.ai/">Napkin.ai</a> to generate this diagram!
Go check out this tool. You can paste your article, and for a given paragraph, it can suggest nice diagrams to represent them.</p></blockquote>
<p>The key steps towards a topic visualization of the stream are:</p>
<ol>
<li><strong>Data acquisition</strong> — The first step is to subscribe to the firehose via WebSockets to gather enough data points to make it interesting to extract trends from them.</li>
<li><strong>Embedding messages</strong> — In order to compare users&rsquo; posts, the text of the posts should be transformed into vector embeddings via an embedding model, which represents posts into a multidimensional space in which distances can be calculated (text whose vector is closer to another one is usually semantically similar).</li>
<li><strong>Clustering messages</strong> — Now that we have all the vector embeddings, a clustering algorithm is used to create groups of messages that are close to each other in vector space, and form a cluster of data points on the same topic.</li>
<li><strong>Generating a summary of clusters&rsquo; messages</strong> — The clustering algorithm grouped messages into different clusters. However, at that point, we don&rsquo;t know what all those grouped messages are about. That&rsquo;s where a generative AI model is called to make sense of those messages, to get a short description of them.</li>
<li><strong>Preparing the data for visualization</strong> — Armed with the clusters of posts and their descriptions, the data for the visualization is prepared.</li>
<li><strong>Visualizing the trends</strong> — The last step is to visualize those clusters of messages with a nice visualization. For that purpose, I decided to present the groups of messages as bubbles (the more posts in a bubble, bigger the bubble is).</li>
</ol>
<h2 id="lets-get-coding">Let&rsquo;s get coding!</h2>
<p>In the article, I&rsquo;ll show only key snippets, sometimes simplifying the code a little bit, but you&rsquo;ll be able to checkout all the code in this <a href="https://github.com/glaforge/bluesky-topic-analysis">Github repository</a>.</p>
<p>As usual, the code will be in Java, and I&rsquo;m going to use my favorite Generative AI framework: <a href="https://docs.langchain4j.dev/">LangChain4j</a>.
For the large language model, my choice went for <a href="https://deepmind.google/technologies/gemini/">Gemini</a>,
and for the embedding model, I&rsquo;m calculating vectors thanks to Google Cloud Vertex AI <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-text-embeddings">embedding models</a>.
Clusters of messages will be created with the <a href="https://commons.apache.org/proper/commons-math/userguide/ml.html">Apache Commons Math</a> library.
The visualization will be implemented in JavaScript with the <a href="https://d3js.org/">D3.js</a> library.</p>
<h3 id="acquiring-bluesky-messages-via-websocket">Acquiring Bluesky messages via WebSocket</h3>
<p>Let&rsquo;s kick off the project by establishing a real-time connection to the Bluesky firehose using WebSockets, thanks to JDK 11&rsquo;s HTTP client.
This allows us to receive a constant stream of public posts as they happen.
The <code>liveMessages()</code> method manages the WebSocket connection and filters incoming messages based on language:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>httpClient.<span style="color:#4070a0">newWebSocketBuilder</span>().<span style="color:#4070a0">buildAsync</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>URI.<span style="color:#4070a0">create</span>(JETSTREAM_WS_ENDPOINT),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>WebSocket.<span style="color:#4070a0">Listener</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>CompletionStage<span style="color:#666">&lt;?&gt;</span><span style="color:#bbb"> </span>onText(WebSocket<span style="color:#bbb"> </span>webSocket,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                    </span>CharSequence<span style="color:#bbb"> </span>data,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                    </span><span style="color:#902000">boolean</span><span style="color:#bbb"> </span>last)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// ... process incoming message ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>});<span style="color:#bbb">
</span></span></span></code></pre></div><p>The <code>onText()</code> method within the <code>WebSocket.Listener</code> is our gateway to the firehose.
Each incoming message, received as a JSON string, needs to be parsed into a usable Java object.
Here&rsquo;s where Google&rsquo;s <code>Gson</code> library and Java <code>record</code>s come into play.
We&rsquo;ve defined a set of nested Java records that mirror the Bluesky message structure:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">Message</span>(Commit<span style="color:#bbb"> </span>commit,<span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>did)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">Commit</span>(Record<span style="color:#bbb"> </span>record,<span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>cid)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">Record</span>(String<span style="color:#bbb"> </span>text,<span style="color:#bbb"> </span>List<span style="color:#666">&lt;</span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>langs,<span style="color:#bbb"> </span>Date<span style="color:#bbb"> </span>createdAt)<span style="color:#bbb"> </span>{}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>These records give us a strongly typed way to access message data.
The Message record holds the actual post content (<code>text</code>), a list of languages (<code>langs</code>),
and the creation timestamp (<code>createdAt</code>), nested within <code>Commit</code> and <code>Record</code> records.
We use Gson to deserialize the JSON strings into these records:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Message<span style="color:#bbb"> </span>message<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GSON.<span style="color:#4070a0">fromJson</span>(String.<span style="color:#4070a0">valueOf</span>(text),<span style="color:#bbb"> </span>Message.<span style="color:#4070a0">class</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><h3 id="calculating-vector-embeddings-for-all-the-messages">Calculating vector embeddings for all the messages</h3>
<p>To analyze the semantic similarity between posts, we convert each post&rsquo;s text into a numerical vector representation, or embedding.
This is achieved using a Vertex AI embedding model, via LangChain4j&rsquo;s <a href="https://docs.langchain4j.dev/integrations/embedding-models/google-vertex-ai/">Vertex AI module</a>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>EmbeddingModel<span style="color:#bbb"> </span>embeddingModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiEmbeddingModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">endpoint</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_VERTEXAI_ENDPOINT&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;text-embedding-005&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">publisher</span>(<span style="color:#4070a0">&#34;google&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>We&rsquo;re using <code>text-embedding-005</code> which is a good embedding model and understands multiple spoken languages
(which is important for analyzing posts coming from a hundred different spoken languages or so).</p>
<p>As embedding all messages takes a while, we&rsquo;re batching the calculation in parallel:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>List<span style="color:#666">&lt;</span>TextSegment<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>allSegments<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>allMessages.<span style="color:#4070a0">stream</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">map</span>(message<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>TextSegment.<span style="color:#4070a0">from</span>(message.<span style="color:#4070a0">commit</span>().<span style="color:#4070a0">record</span>().<span style="color:#4070a0">text</span>()))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">toList</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>List<span style="color:#666">&lt;</span>Embedding<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>allEmbeddings<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>IntStream.<span style="color:#4070a0">range</span>(0,<span style="color:#bbb"> </span>numberOfParallelBatches)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">parallel</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">mapToObj</span>(i<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>embeddingModel.<span style="color:#4070a0">embedAll</span>(allSegments...)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">flatMap</span>(List::stream)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">toList</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><h3 id="creating-clusters-of-posts">Creating clusters of posts</h3>
<p>With embeddings in hand, we can now group similar posts together using the <a href="https://en.wikipedia.org/wiki/DBSCAN">DBSCAN</a>
clustering algorithm (Density-based spatial clustering of applications with noise) :</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>clusters<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>DBSCANClusterer<span style="color:#666">&lt;</span>ClusterableEmbeddedMessage<span style="color:#666">&gt;</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>MAXIMUM_NEIGHBORHOOD_RADIUS,<span style="color:#bbb"> </span>MINIMUM_POINTS_PER_CLUSTER)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">cluster</span>(clusterableEmbeddedMessages);<span style="color:#bbb">
</span></span></span></code></pre></div><p>For 10k posts, using a minimum of 10 points per cluster sounds about right.
As a rule of thumb, I got good visualizations with one cluster point per 1k messages
(ie. 10 points per cluser for 10k messages, 20 points per cluster for 20k messages).</p>
<p>The maximum neighborhood radius at 0.5 also looked like a good value.
I tried smaller and bigger values, but either the cluster are too specific and narrow with low values,
or too broad and generalist with higher values.</p>
<p>It&rsquo;s important to check for yourself the <em>hyperparameters</em> of the algorithms you chose for your use case.
Some values might be better than others, and they are very much use-case dependant.
There&rsquo;s no magic numbers, you have to experiment to find the right mix for you!</p>
<p>Using a different embedding model (like <code>text-multilingual-embedding-002</code>), reducing the dimensionality to 128 dimensions,
I had to use a max neighborhood radius of 0.2 instead, to get a good number of clusters.</p>
<h3 id="generating-a-description-for-clusters-of-messages">Generating a description for clusters of messages</h3>
<p>At this point, we have topic clusters. But they&rsquo;re just bags of numbers without a real meaning for us, human beings.
What we need is a way to make sense of those clusters, to know what topic they cover.</p>
<p>We configure the Vertex AI Gemini model, thanks to LangChain4j&rsquo;s <a href="https://docs.langchain4j.dev/integrations/language-models/google-vertex-ai-gemini">Gemini module</a>, with a max number of tokens, to avoid situations where a topic description is too long:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ChatLanguageModel<span style="color:#bbb"> </span>chatModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//  .modelName(&#34;gemini-2.0-flash-exp&#34;)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash-002&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">maxOutputTokens</span>(25)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>You can use both the latest Gemini 1.5 Flash, or the new 2.0 Flash experimental model.
If you&rsquo;re hitting quota limits, as 2.0 is currently only in preview, 1.5 will give great results too.</p>
<p>To make the clusters more understandable, we call Gemini to generate a concise summary for each cluster,
passing all the messages contained in that cluster:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>modelResponse<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>chatModel.<span style="color:#4070a0">generate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>SystemMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Summarize the following list of social media messages in one
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        simple description. Don&#39;t give a full sentence saying the
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        social messages are about a topic, just give the topic
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        directly in 10 words or less, without mentioning the
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        messages are social media posts or reactions.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>UserMessage.<span style="color:#4070a0">from</span>(appendedMessages)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>When I was running this code on January 1st, I was seeing topics like <code>New Year's greetings and well wishes</code> or
<code>Happy New Year 2025 wishes and hopeful sentiments for the year</code>.
But some categories of topics often come back, like a big cluster of emojis expressing various expressions,
or people sharing video links on YouTube, or pictures from Instagram.
I also saw some interesting trends as they came up, like weather alerts for snow storms,
or someone famous receiving congratulations for announcing some anniversary.
There are also repeated posts tagging people to request funding for some cause.
Funnily, in the morning, I was often seeing people sharing in how many steps
they solved the <a href="https://www.nytimes.com/games/wordle/index.html">Wordle</a> word puzzle!</p>
<p>I filtered the messages to analyze only English messages for the purpose of this demo,
but there are a bunch of users setting their language as English, but posting in another language.
However it&rsquo;s not really a problem for Gemini which happily handles more than a hundred spoken languages.</p>
<h3 id="preparing-the-data-for-visualization">Preparing the data for visualization</h3>
<p>The cluster summaries and their sizes (number of posts) are then formatted as JSON data, for ingestion by D3.js:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> data <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>  name<span style="color:#666">:</span> <span style="color:#4070a0">&#34;Bluesky topic clusters&#34;</span>,
</span></span><span style="display:flex;"><span>  children<span style="color:#666">:</span> [
</span></span><span style="display:flex;"><span>    {name<span style="color:#666">:</span> <span style="color:#4070a0">&#34;Summary of Cluster 1&#34;</span>, value<span style="color:#666">:</span> <span style="color:#40a070">396</span>},
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// ... other clusters
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>  ]
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>This JSON structure is ideal for consumption by D3.js, which we&rsquo;ll use for visualization.
The <code>FirehoseConsumer</code> class writes this JSON data to the <code>newdata.js</code> file,
which is integrated in the static web assets and loaded by D3.</p>
<h3 id="visualizing-the-data-with-d3js">Visualizing the data with D3.js</h3>
<p>Finally, the <code>visualisation.js</code> script uses D3.js to create an interactive bubble chart.
Each bubble represents a cluster, with its surface area corresponding to the number of posts in that cluster.
The color of the circles is also dynamically generated:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span><span style="color:#bbb"> </span>colorScale<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>d3.<span style="color:#4070a0">scaleQuantize</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">domain</span>(<span style="color:#666">[</span>0,<span style="color:#bbb"> </span>maxValue<span style="color:#666">]</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">range</span>(colorPalette);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">//.. later, in the circle</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">attr</span>(<span style="color:#4070a0">&#34;fill&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>d<span style="color:#bbb"> </span><span style="color:#666">=&gt;</span><span style="color:#bbb"> </span>d.<span style="color:#4070a0">children</span><span style="color:#bbb"> </span><span style="color:#666">==</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span><span style="color:#666">?</span><span style="color:#bbb"> </span>colorScale(d.<span style="color:#4070a0">r</span>)<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;#fefef0&#34;</span>)<span style="color:#bbb">
</span></span></span></code></pre></div><p>What&rsquo;s more interesting in this part of the project is how the visualization is created.
I was inspired by the circle packing visualization seen in this <a href="https://observablehq.com/@d3/pack/2">article</a>,
which uses D3.js&rsquo;s <a href="https://d3js.org/d3-hierarchy/pack">circle packing layout method</a>.
I borrowed heavily from this example, and tweaked it for my needs, and to my liking.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span><span style="color:#bbb"> </span>pack<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>d3.<span style="color:#4070a0">pack</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">size</span>(<span style="color:#666">[</span>width<span style="color:#bbb"> </span><span style="color:#666">-</span><span style="color:#bbb"> </span>margin<span style="color:#bbb"> </span><span style="color:#666">*</span><span style="color:#bbb"> </span>2,<span style="color:#bbb"> </span>height<span style="color:#bbb"> </span><span style="color:#666">-</span><span style="color:#bbb"> </span>margin<span style="color:#bbb"> </span><span style="color:#666">*</span><span style="color:#bbb"> </span>2<span style="color:#666">]</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">padding</span>(4);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">const</span><span style="color:#bbb"> </span>root<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>pack(d3.<span style="color:#4070a0">hierarchy</span>(data)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">sum</span>(d<span style="color:#bbb"> </span><span style="color:#666">=&gt;</span><span style="color:#bbb"> </span>d.<span style="color:#4070a0">value</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">sort</span>((a,<span style="color:#bbb"> </span>b)<span style="color:#bbb"> </span><span style="color:#666">=&gt;</span><span style="color:#bbb"> </span>b.<span style="color:#4070a0">value</span><span style="color:#bbb"> </span><span style="color:#666">-</span><span style="color:#bbb"> </span>a.<span style="color:#4070a0">value</span>));<span style="color:#bbb">
</span></span></span></code></pre></div><p>The tricky part, as well, was how to render and layout the text of the topics, along with the number of posts per cluster, inside each circle.
I got it working by appending a custom div, as a <em>foreign object</em> in the SVG document, and by tweaking the positioning:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>node.filter(d =&gt; <span style="color:#666">!</span>d.children)
</span></span><span style="display:flex;"><span>    .append(<span style="color:#4070a0">&#34;foreignObject&#34;</span>)
</span></span><span style="display:flex;"><span>    .attr(<span style="color:#4070a0">&#34;x&#34;</span>, d =&gt; <span style="color:#666">-</span><span style="color:#40a070">0.8</span> <span style="color:#666">*</span> d.r) <span style="color:#60a0b0;font-style:italic">// center horizontally
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    .attr(<span style="color:#4070a0">&#34;y&#34;</span>, d =&gt; <span style="color:#666">-</span><span style="color:#40a070">1.1</span><span style="color:#666">*</span>d.r) <span style="color:#60a0b0;font-style:italic">// center vertically, manually adjusted
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    .attr(<span style="color:#4070a0">&#34;width&#34;</span>, d =&gt; <span style="color:#40a070">1.6</span> <span style="color:#666">*</span> d.r)
</span></span><span style="display:flex;"><span>    .attr(<span style="color:#4070a0">&#34;height&#34;</span>, d =&gt; <span style="color:#40a070">2</span> <span style="color:#666">*</span> d.r)
</span></span><span style="display:flex;"><span>    .append(<span style="color:#4070a0">&#34;xhtml:div&#34;</span>)
</span></span><span style="display:flex;"><span>    .classed(<span style="color:#4070a0">&#34;foreignDiv&#34;</span>, <span style="color:#007020;font-weight:bold">true</span>)
</span></span><span style="display:flex;"><span>    .style(<span style="color:#4070a0">&#34;font-size&#34;</span>, d =&gt; d.r <span style="color:#666">/</span> <span style="color:#40a070">5.3</span> <span style="color:#666">+</span> <span style="color:#4070a0">&#34;px&#34;</span>) <span style="color:#60a0b0;font-style:italic">// dynamic font sizing
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    .html(d =&gt;
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#34;&lt;span style=&#39;font-size: &#34;</span> <span style="color:#666">+</span> (d.r <span style="color:#666">/</span> <span style="color:#40a070">2.5</span>) <span style="color:#666">+</span> <span style="color:#4070a0">&#34;px; color: &#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">+</span> d3.color(colorScale(d.r)).darker(<span style="color:#40a070">1</span>) <span style="color:#666">+</span> <span style="color:#4070a0">&#34;;&#39;&gt;&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">+</span> format(d.value)
</span></span><span style="display:flex;"><span>        <span style="color:#666">+</span> <span style="color:#4070a0">&#34;&lt;/span&gt;&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">+</span> d.data.name
</span></span><span style="display:flex;"><span>        <span style="color:#666">+</span> <span style="color:#4070a0">&#34;&lt;br/&gt;&#34;</span>
</span></span><span style="display:flex;"><span>    );
</span></span></code></pre></div><p>Lots of hard-coded values to make it look nice!</p>
<p>To put everything together: an HTML file imports D3.js, our <code>newdata.js</code> file containing the cluster definitions,
the <code>visualization.js</code> file creates the bubble chart, plus some CSS in <code>styles.css</code>.
And when running the Java class, the <code>newdata.js</code> is generated and updated in the <code>static</code> web asset folder.</p>
<h2 id="experiments-and-what-else-to-explore">Experiments, and what else to explore</h2>
<h3 id="no-live-demo-available">No live demo available</h3>
<p>Interesting topic visualizations happen when you have collected enough messages to analyze.
Gathering about 10 thousand posts seemed to offer good results, but in spite of the 10+ million users on Bluesky,
you still need about 4 or 5 minutes to store that many messages.
Without mentioning the time it takes to calculate the embeddings (about 30 seconds in parallel),
and the clustering algorithm (about 1 minute and a half with a runtime complexity of <code>n*log(n)</code>).
So this is not ideal for a <em>real-time</em> analysis of the current trending topics.
That&rsquo;s why I haven&rsquo;t posted a demo application online, as it&rsquo;s too slow to wait for the result to appear on screen.</p>
<p>What might be interesting to explore is somehow a live updating view that would be re-calculated every couple of minutes or so,
over a sliding window of messages, but the clustering duration is still a problem.
However, it&rsquo;s also something that could quickly become costly, considering the number of embedding calculations and generative summaries to generate each time.</p>
<h3 id="different-embedding-models">Different embedding models</h3>
<p>Before parallelizing / batching the vector embedding calculations (which still take half a minute),
I also tried a non-cloud hosted embedding model, like a quantized version of the
<a href="https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2">all-MiniLM-L6-v2</a> embedding model, which can run locally without a big GPU.
I used it in some other projects with success, but for this clustering exercise, I found the result of poor quality, as if it wasn&rsquo;t knowledgeable enough to discern different topics.</p>
<p>I paid attention to restricting the messages to only English messages, as I knew that that small model was more at ease with English, but that didn&rsquo;t really help.
Ideally, I&rsquo;d like to find a fast embedding model with good classification capabilities.
But read on, for another idea on speeding up the clustering part of the equation.</p>
<h3 id="different-clustering-algorithms">Different clustering algorithms</h3>
<p>DBSCAN isn&rsquo;t super fast, with a <code>n*log(n)</code> runtime complexity.</p>
<p>Apache Commons Math also offers a <a href="https://en.wikipedia.org/wiki/K-means%2B%2B">KMeans++</a> implementation that is faster (with a more linear runtime)
but the <code>k</code> hyperparameter to specify is always giving a fixed number of clusters.
One one hand, it&rsquo;s nice to have a more predictable visualization (neither too few, nor too many bubbles with small text to display),
on the other hand, the fact the number of clusters si set in stone, leads the clusters to be too generic and too broad,
and there&rsquo;s always one cluster that contains everything that couldn&rsquo;t be clustered in meaningful groups.</p>
<p>In spite of its runtime complexity, I like DBSCAN for the fact it creates quite diverse but acurate clusters,
as it figures itself how many clusters to create, depending on the various topics it&rsquo;ll come across.</p>
<p>There&rsquo;s another library that I&rsquo;d like to try some day, that&rsquo;s <a href="https://haifengl.github.io/clustering.html">Smile</a>.
It supports even more clustering algorithms than Apache Commons Math.</p>
<p>Something interesting going on for Smile is also its <a href="https://haifengl.github.io/manifold.html#t-sne">dimensionality reduction algorithms</a>
(that they call <em>manifold learning</em>) like <a href="https://en.wikipedia.org/wiki/T-distributed_stochastic_neighbor_embedding">t-SNE</a> and
<a href="https://en.wikipedia.org/wiki/Nonlinear_dimensionality_reduction#Uniform_manifold_approximation_and_projection">UMAP</a>.</p>
<p>Why am I mentioning dimensionality reduction?
For one, it&rsquo;s super handy for visualizing the clusters in 2D or 3D.
But another idea I wanted to try was that if the reduction is fast enough,
maybe applying the clustering algorithm on lower-dimensioned data would be much faster.
The <em>projection</em> (reducing the dimensionality) before clustering approach is also the one this <a href="https://github.com/huggingface/text-clustering">project from HuggingFace</a>
followed to cluster the <a href="https://huggingface.co/datasets/HuggingFaceTB/cosmopedia">Cosmopedia</a> dataset.</p>
<p>Indeed, Vertex AI embeddings generate vectors of 768 dimensions.
That said, some of the Vertex AI embeddings are <a href="https://huggingface.co/blog/matryoshka"><em>Matryoshka</em> embeddings</a>,
so we could also calculate clusters on truncated vectors, without losing too much accuracy, without even doing dimenstionality reduction!
Both <code>text-embedding-005</code> and <code>text-multilingual-embedding-002</code> support reducing the vector dimension, so it&rsquo;s worth trying.
You just need to set <code>outputDimensionality(128)</code> on the embedding model builder to reduce the dimensions down to 128.
Then the clustering time can be go down to 15 seconds instead of 80 seconds like with full 768-dimension vectors.</p>
<h3 id="what-else-to-try">What else to try?</h3>
<ul>
<li>In this experiment, I analyzed text, but users post hashtags, pictures, links, on their profiles.
It might be interesting to look at what is trending in terms of hashtags, or analyze the sentiment of messages related to such a hashtag.</li>
<li>Looking at links, maybe it&rsquo;d be interesting to also see what is shared, which news article is more popular&hellip;</li>
<li>Regarding pictures, we could perhaps see which animals are more trendy? And do some fun analysis of favorite animals in different countries&hellip;</li>
<li>Another interesting analysis could be to cluster user profiles, to find users posting on the same topics.</li>
<li>I&rsquo;d like to think more about how to make this application more lively, and make users explore indvidual posts contained in each clusters.</li>
</ul>
<p>Many more things to try out and explore!</p>
<h2 id="summary">Summary</h2>
<p>The generated visualization offers an intuitive and engaging way to explore the <strong>trending topics on Bluesky</strong>.
And <strong>generative AI</strong> tools like <strong>Gemini</strong> and <strong>Vertex AI</strong> are here to help creating such data explorations.</p>
<p>This project combines the power of <strong>real-time data streaming</strong>, <strong>AI-driven analysis</strong>, and (not-yet-interactive) <strong>visualization</strong> to provide a valuable tool for understanding the ever-evolving conversations on Bluesky.
It sets the stage for more sophisticated analysis, such as tracking topic evolution over time, sentiment analysis within clusters, and identification of key influencers within specific discussions.</p>
<p>As always, this project also confirmed that <strong>Java</strong> and <strong><a href="https://docs.langchain4j.dev/">LangChain4j</a></strong> are my two best buddies to explore topics with generative AI approaches (no need for Python!)
And I was happy to use <a href="https://d3js.org/">D3.js</a> again for visualization purposes.
It&rsquo;s not easy to master, but it&rsquo;s a super powerful library!
I&rsquo;m also glad that <a href="https://cloud.google.com/products/gemini/code-assist?e=0&amp;hl=en">Gemini Code Assist</a> helped me work with D3.js, to develop and enhance the visualization.</p>
<p>Finally, of course, the <strong>Gemini chat model</strong> and <strong>Vertex AI embedding model</strong> were perfect for the task, giving high quality embedding vectors, and clear synthetic summaries of social media posts.</p>
<p>Don&rsquo;t hesitate to <a href="https://github.com/glaforge/bluesky-topic-analysis">check out the code</a> and play with this project!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Let's think with Gemini Flash 2.0's experimental thinking mode and LangChain4j</title><link>https://glaforge.dev/posts/2024/12/20/lets-think-with-gemini-2-thinking-mode-and-langchain4j/</link><pubDate>Fri, 20 Dec 2024 16:14:21 +0100</pubDate><guid>https://glaforge.dev/posts/2024/12/20/lets-think-with-gemini-2-thinking-mode-and-langchain4j/</guid><description>&lt;p>Yesterday, Google released yet another cool Gemini model update, with &lt;strong>Gemini 2.0 Flash &lt;a href="https://ai.google.dev/gemini-api/docs/thinking-mode">thinking mode&lt;/a>&lt;/strong>.
Integrating natively and transparently some &lt;a href="https://www.promptingguide.ai/techniques/cot">chain of thought&lt;/a> techniques,
the model is able to take some more thinking time, and automatically decomposes a complex task into smaller steps, and explores various paths in its thinking process.
Thanks to this approach, Gemini 2.0 Flash is able to solve more complex problems than Gemini 1.5 Pro or the recent Gemini 2.0 Flash experiment.&lt;/p></description><content:encoded>
<![CDATA[<p>Yesterday, Google released yet another cool Gemini model update, with <strong>Gemini 2.0 Flash <a href="https://ai.google.dev/gemini-api/docs/thinking-mode">thinking mode</a></strong>.
Integrating natively and transparently some <a href="https://www.promptingguide.ai/techniques/cot">chain of thought</a> techniques,
the model is able to take some more thinking time, and automatically decomposes a complex task into smaller steps, and explores various paths in its thinking process.
Thanks to this approach, Gemini 2.0 Flash is able to solve more complex problems than Gemini 1.5 Pro or the recent Gemini 2.0 Flash experiment.</p>
<p><strong>And of course, you can already use Gemini 2.0 Flash Thinking with <a href="https://docs.langchain4j.dev/">LangChain4j</a>!</strong></p>
<p>So let&rsquo;s <em>delve</em> into some examples together!</p>
<h2 id="solving-a-riddle">Solving a riddle</h2>
<p>I found some <a href="https://parade.com/947956/parade/riddles/">riddles online</a> and thought I would try one with both the new Gemini 2.0 Flash model,
and the new thinking flavor as well, in order to contrast their way of working, and the outcome.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GOOGLE_AI_GEMINI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash-exp&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        The day before yesterday I was 21, and next year I will be 24.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        When is my birthday?
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Gemini replies with:</p>
<pre tabindex="0"><code>This is a classic riddle! Here&#39;s how to solve it:

* **&#34;The day before yesterday I was 21&#34;** means yesterday you were 22.
* **&#34;Next year I will be 24&#34;** means you are currently 23.
* If you were 22 yesterday and 23 today, your birthday must be **today**.

Therefore, your birthday is **today**.
</code></pre><p>The answer is good, it&rsquo;s today, but it doesn&rsquo;t really tell exactly which date is the birthday.</p>
<p>Now let&rsquo;s update the model name to use the new thinking mode:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GOOGLE_AI_GEMINI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash-thinking-exp-1219&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        The day before yesterday I was 21, and next year I will be 24.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        When is my birthday?
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Gemini advanced thinking process shows up:</p>
<pre tabindex="0"><code>Here&#39;s a thinking process to solve this riddle:

1. **Analyze the core statements:**
    * &#34;The day before yesterday I was 21.&#34;
    * &#34;Next year I will be 24.&#34;

2. **Break down the time references:**
Identify the different points in time being referenced.
    * &#34;The day before yesterday&#34; refers to a past date.
    * &#34;Yesterday&#34; refers to the day after &#34;the day before yesterday&#34;.
    * &#34;Today&#34; is the present day.
    * &#34;Tomorrow&#34; is the day after today.
    * &#34;Next year&#34; refers to the year after the current year.

3. **Assign ages to the time references:**  Based on the statements,
assign ages to the person at these different time points.
    * The day before yesterday: 21 years old.
    * Yesterday: Since the person&#39;s age changes by one each birthday,
     yesterday they were either still 21 or had just turned 22.
    * Today: Continuing the progression, today they are either 22
      or 23.
    * Next year: 24 years old.

4. **Focus on the age jump:**  The key is the jump in age from
&#34;today&#34; to &#34;next year.&#34; To go from being 22 or 23 today to being 24
next year *definitively* means their birthday must fall *between*
now and next year.

5. **Work backward from &#34;next year&#34;:** If they will be 24 next year,
they must turn 24 sometime within that year. This means they are
currently 23 *or* they are still 23 and about to turn 24 soon.

6. **Work forward from &#34;the day before yesterday&#34;:**
If they were 21 the day before yesterday, then yesterday they
turned 22. This means today they are 22.

7. **Reconcile the information:** We have two possibilities for the
current age: 22 (from the first statement) and potentially 23
(considering the jump to 24 next year). The only way both statements
can be true is if the birthday is *very* soon.

8. **Consider the implications of the age progression:**
    * If today they are 22, and next year they will be 24,
      the birthday must be *before* the end of the current year.
    * Since they were 21 the day before yesterday, they must have
      turned 22 *yesterday*.

9. **Determine the date:**  If they turned 22 yesterday,
and the statement refers to &#34;next year I will be 24,&#34; this can only
happen if today is the *last day* of the year, and their birthday
is *today*.

10. **Verify the solution:** Let&#39;s check if this works:
    * If today is December 31st:
        * The day before yesterday (December 29th) they were 21.
        * Yesterday (December 30th) they turned 22.
        * Today (December 31st) they are 22.
        * Next year (after December 31st),
          they will turn 23 and then 24. This works!

11. **Formalize the answer:** The birthday is on December 31st.

This is a classic riddle that plays on how we perceive time.
Here&#39;s how to solve it:

* **&#34;The day before yesterday I was 21&#34;**:
  This means yesterday you were 22.
* **&#34;Next year I will be 24&#34;**: This means you are currently 23.

For both of these statements to be true,
today must be **January 1st**.

Let&#39;s break it down:

* **January 1st:** You are 23.
* **December 31st (yesterday):** You were 22.
* **December 30th (the day before yesterday):** You were 21.
* **Sometime later this year (your birthday):** You will turn 24.

Therefore, your birthday is on **December 31st**.
</code></pre><p>Now, thanks to this advanced reasoning approach, it&rsquo;s able to tell me the exact date of the birthday.</p>
<p>I&rsquo;ve also tried the model&rsquo;s reasoning capabilities on coding problems (puzzles like Advent of Code ones),
and it&rsquo;s really doing a great job at solving most of them!</p>
<h2 id="multimodal-reasoning">Multimodal reasoning</h2>
<p>Remember that Gemini is a <strong>multimodal</strong> model?
That means it can accept other <em>modalities</em> like images, in addition to text.</p>
<p>So let&rsquo;s have a look at another example, with a word game from the
<a href="https://www.rd.com/article/visual-brain-teasers-and-puzzles/">Reader&rsquo;s digest</a>.
Only one of the rows is an anagram of a five-letter English word&hellip;</p>
<p><figure>
  <a href="#img-0196b85b7859d34b33551341b04bea49">
    <img src="/img/gemini/word-puzzle.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-0196b85b7859d34b33551341b04bea49">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/word-puzzle.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>This time, it&rsquo;s a word game, but the information is inside an image.
So we need to pass both the image, and the problem statement.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GOOGLE_AI_GEMINI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash-thinking-exp-1219&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>ImageContent.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>String(Base64.<span style="color:#4070a0">getEncoder</span>().<span style="color:#4070a0">encode</span>(readBytes(FILE_URI))),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;image/png&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>TextContent.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Only one of the rows in the picture is an anagram of a
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            five-letter English word. Do you know which one it is?
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>));<span style="color:#bbb">
</span></span></span></code></pre></div><p>If I ask that puzzle to Gemini 2.0 Flash (by changing the model name used above), it&rsquo;ll tell me (quickly) that:</p>
<pre tabindex="0"><code>Certainly!
The anagram in the image is **O L C R E**,
which unscrambled is **COLOR**.
</code></pre><p>Gemini 2.0 Flash is at the very top of the LLM <a href="https://lmarena.ai/?leaderboard">leaderboard</a>
but it gets the answer wrong. Notice that <code>COLOR</code> is not an anagram of <code>OLCRE</code> at all.</p>
<p>Now let&rsquo;s see what the <em>thinking</em> model says:</p>
<pre tabindex="0"><code>The user wants me to find the row that is an anagram of a five-letter
English word. I need to go through each row and see if I can
rearrange the letters to form a valid five-letter word.

Row 1: L E G T N - Could be GENTL but need 5 letters. Could be GLENT,
but is not a common word.
Row 2: O L C R E - Could be CLORE, COLER, RECOL. RECOL is the most
promising one.
Row 3: T C O H E - Could be ECHOT.
Row 4: L U D I B - Could be BUILD.
Row 5: M E U D Q - No obvious combination.
Row 6: H I N W L - Could be WHILN.
Row 7: W E S O L - Could be LOWES, OWELS, SOWEL, WOLES.

From the analysis, RECOL seems to be a valid word and has 5 letters.
Let me double check other possibilities.
Row 1: L E G T N -  TENGLE? No.
Row 3: T C O H E -  Could be CHETO, but not a common word.
Row 4: L U D I B -  Could be BLUID, but not a common word.
Row 6: H I N W L -  Could be WHILE.

Out of the possible options, WHILE and RECOL are valid five-letter
words. Let me re-examine the instruction &#34;Only one of the rows in
the picture is an anagram of a five-letter English word.&#34;

Row 1: L E G T N - No obvious five-letter word.
Row 2: O L C R E - CLORE, COLER, RECOL
Row 3: T C O H E - ECHOT
Row 4: L U D I B - BUILD
Row 5: M E U D Q - No obvious five-letter word.
Row 6: H I N W L - WHILE
Row 7: W E S O L - LOWES, OWELS, SOWEL, WOLES

There seems to be more than one. Let me re-check the letter counts.
 All rows have 5 letters.

Rechecking my anagram findings:
Row 2: O L C R E -  Could form RECOL
Row 3: T C O H E - Could form ECHOT
Row 4: L U D I B - Could form BUILD
Row 6: H I N W L - Could form WHILE
Row 7: W E S O L - Could form LOWES

Let me think again. The instruction says *only one*. I might be making
a mistake in identifying a valid word.

Re-examining:
Row 1: L E G T N - No common five-letter word.
Row 2: O L C R E - RECOL
Row 3: T C O H E - ECHOT
Row 4: L U D I B - BUILD
Row 5: M E U D Q - No common five-letter word.
Row 6: H I N W L - WHILE
Row 7: W E S O L - LOWES

Perhaps some of these are not valid English words.
Let me verify the words.
RECOL -  Yes, it&#39;s a valid word.
ECHOT - No, ECHO is a word, ECHOT is not.
BUILD - Yes, it&#39;s a valid word.
WHILE - Yes, it&#39;s a valid word.
LOWES - Yes, it&#39;s a valid word.

I am still finding more than one valid word. Let me go through each
row very carefully again.

Row 1: L E G T N - No obvious 5-letter word
Row 2: O L C R E - Could be CLORE, COLER, RECOL.
Row 3: T C O H E - Could be CHOTE. No such word.
Row 4: L U D I B - Could be BUILD.
Row 5: M E U D Q - No obvious 5-letter word.
Row 6: H I N W L - Could be WHILE.
Row 7: W E S O L - Could be LOWES.

Let me verify the validity of each potential word.
RECOL - Valid English word.
BUILD - Valid English word.
WHILE - Valid English word.
LOWES - Valid English word (name of a store).

I am still struggling to find only one. Perhaps I am overlooking
something simple. Let me reread the question carefully.
&#34;Only one of the rows in the picture is an anagram of a five-letter
English word.&#34;

Let me try rearranging the letters systematically for each row.

Row 1: L E G T N -  No five letter word.
Row 2: O L C R E -  RECOL
Row 3: T C O H E - No five letter word.
Row 4: L U D I B -  BUILD
Row 5: M E U D Q - No five letter word.
Row 6: H I N W L -  WHILE
Row 7: W E S O L -  LOWES

I am still identifying multiple valid words. Could there be a
constraint I am missing? The hints are just crops, so they don&#39;t
add new information.

Let me consider uncommon words.

Row 2: O L C R E -  Could be ORCEL.
Row 3: T C O H E -
Row 7: W E S O L - Could be OLWES.

I am still finding multiple possibilities. The question states
*only one*. This implies that my initial assessment of multiple
possibilities is incorrect. Let me re-examine each row with more
scrutiny for common five-letter words.

Row 1: L E G T N - No.
Row 2: O L C R E - RECOL.
Row 3: T C O H E - No.
Row 4: L U D I B - BUILD.
Row 5: M E U D Q - No.
Row 6: H I N W L - WHILE.
Row 7: W E S O L - LOWES.

I have identified four potential candidates. There must be something
I am not considering. Is there a specific type of five-letter word?
No constraint mentioned.

Let me check my understanding of anagram. An anagram is a
rearrangement of letters to form a new word.

Perhaps some of the words I identified are not standard English words.

RECOL - Yes, it is.
BUILD - Yes, it is.
WHILE - Yes, it is.
LOWES - Yes, it is.

I am still at a loss. The instruction is very clear: &#34;Only one of the
rows... is an anagram of a five-letter English word.&#34;

Let me try a different approach. Assume only one is correct
and try to disprove the others.

If Row 2 (O L C R E - RECOL) is the answer, then none of the others
should form a valid five-letter word.
Row 4 (L U D I B) - BUILD is a valid word. This contradicts the
&#34;only one&#34; rule.

If Row 4 (L U D I B - BUILD) is the answer, then none of the others
should form a valid five-letter word.
Row 2 (O L C R E) - RECOL is a valid word. This contradicts the
&#34;only one&#34; rule.

If Row 6 (H I N W L - WHILE) is the answer, then none of the others
should form a valid five-letter word.
Row 2 (O L C R E) - RECOL is a valid word. This contradicts the
&#34;only one&#34; rule.

If Row 7 (W E S O L - LOWES) is the answer, then none of the others
should form a valid five-letter word.
Row 2 (O L C R E) - RECOL is a valid word. This contradicts the
&#34;only one&#34; rule.

There must be something very obvious I am missing.

Let me read the rows aloud and see if something jumps out.
L E G T N
O L C R E
T C O H E
L U D I B
M E U D Q
H I N W L
W E S O L

Is it possible that some of the letter combinations seem like a word
but aren&#39;t actually?

Let&#39;s focus on making sure each identified word is definitively
correct.

RECOL - Define: To collect again. Yes, it&#39;s a valid word.
BUILD - Define: To construct. Yes, it&#39;s a valid word.
WHILE - Define: A period of time. Yes, it&#39;s a valid word.
LOWES -  While primarily known as a store name, it can be
a plural noun.
However, the question is about anagrams of five-letter words.

Let&#39;s go back to each row and try to form words methodically.

Row 1: L E G T N -  Tingle, Longe, Glent. None are common five-letter
words.
Row 2: O L C R E -  Clore, Coler, Lorec, Recol. RECOL is a valid word.
Row 3: T C O H E -  Chote, Cothe, EchoT. None are common five-letter
words.
Row 4: L U D I B -  Bluid, Build. BUILD is a valid word.
Row 5: M E U D Q - No valid word.
Row 6: H I N W L -  Whiln, While. WHILE is a valid word.
Row 7: W E S O L -  Lowes, Owels, Sowle, Woles. LOWES is a valid word.

The problem states *only one*. This is the key.
There must be something about one of these words that makes it
the correct answer over the others.

Could it be about the commonality of the word?  While all are valid,
some are more frequently used.

RECOL - Less common.
BUILD - Very common.
WHILE - Very common.
LOWES - Common (as a store name).

Could there be a nuance in the definition or usage?

Let me consider if the capitalization or the &#34;RD&#34; at the bottom has
any relevance (unlikely for an anagram puzzle).

The crops are there to help see better, so the visual aspect
is important.

Let me look at the letters themselves and see if there&#39;s a pattern
or something I&#39;ve missed.

Maybe one of the identified words isn&#39;t as standard as the others
in a pure linguistic sense.

Consider &#34;LOWES&#34;. It&#39;s primarily a proper noun. Is the puzzle looking
for common nouns or verbs?  The question says &#34;a five-letter English
word,&#34; which is quite broad.

Let me revisit the less common words I dismissed earlier.

Row 1: L E G T N - GLENT (a small secluded valley).
Row 3: T C O H E -  None.
Row 6: H I N W L -  WHILN (the pod or husk of seeds).

If GLENT is considered, we have another possibility.
If WHILN is considered, another possibility.

The constraint &#34;only one&#34; is crucial.

Let&#39;s assume, for a moment, that the answer is indeed one of the ones
I identified and I need to find a reason to exclude the others.

Could the structure of the letter arrangement provide a clue?
No obvious pattern.

Let me focus on the most common five-letter words from my list:
BUILD and WHILE. If one of these is the answer, why isn&#39;t the other?

Perhaps there&#39;s a very simple word I&#39;m overlooking in one of the rows.

Row 3: T C O H E -  THROE.

THROE is a valid five-letter word.

Now my list is: RECOL, BUILD, WHILE, LOWES, THROE.
Still more than one.

I am stuck on the &#34;only one&#34; constraint. What could make one row
uniquely solvable?

Could it be about pronunciation or homophones?
Unlikely for an anagram.

Let me go back to the instructions and the image.
There are no additional instructions.

Is it possible there&#39;s a very obscure five-letter English word?

Let&#39;s review each row and try all possible combinations.
This will be tedious, but the &#34;only one&#34; constraint suggests
a unique solution.

Row 1: L E G T N -  ELTNG, ELNTG, ETLNG, ETNLG, ENLTG, ENTLG...
This is not efficient.

Let me rely on my vocabulary for common words first.

Consider the possibility that one of the words I identified isn&#39;t
a standard English word in the context of this puzzle.

Could &#34;LOWES&#34; as a store name not count? But the letters
can form the word.

Let&#39;s try working backward from the assumption that only one row
is correct. If I pick a row, can I definitively say the others
don&#39;t form any five-letter words?

If Row 2 (O L C R E) is the answer (RECOL), then the others cannot
form a valid five-letter word. But Row 4 (BUILD), Row 6 (WHILE),
and Row 7 (LOWES) do.

There must be a constraint I am not understanding
or a word I am misinterpreting.

Could it be about the frequency of letter usage in the
English language? Unlikely.

Let me consider common knowledge or general vocabulary.

Perhaps one of the letter sets can *only* form one five-letter word,
while others can form multiple.

Row 2: O L C R E - RECOL, CLORE, COLER.
Row 4: L U D I B - BUILD. Only one common word.
Row 6: H I N W L - WHILE. Only one common word.
Row 7: W E S O L - LOWES, OWELS, SOWLE, WOLES.

This is a potential lead. Row 4 and Row 6 seem to have a single,
very common five-letter word.

Let me double-check Row 4 (L U D I B). Are there other common
five-letter words?  BLUID is not common.

Let me double-check Row 6 (H I N W L). Are there other common
five-letter words? WHILN is less common.

The wording &#34;anagram of a five-letter English word&#34; is precise.

Could it be that some of the words I found are plurals or other forms
that don&#39;t fit the strict definition?  No, RECOL, BUILD, WHILE
are base forms.

Let me consider the visual arrangement again.
Is there any hidden message or pattern?  The lines separate the rows.

The &#34;RD&#34; in the corner is likely just a watermark.

The color is uniform.

Back to the anagrams. The &#34;only one&#34; is the key.

If I had to pick one, which row is *unambiguously*
a single five-letter English word?

Row 4: L U D I B - BUILD. It&#39;s hard to make another common
five-letter word from these letters.

Let&#39;s confirm this. Consider permutations.
BLUID is the only other plausible one, and it&#39;s not common.

Could the answer be BUILD based on this uniqueness?

Let me test this hypothesis against the other rows.

Row 2: O L C R E - RECOL, CLORE, COLER. Multiple options.
Row 6: H I N W L - WHILE. WHILN is another possibility,
though less common.
Row 7: W E S O L - LOWES, OWELS, etc. Multiple options.

This line of reasoning seems promising.
The &#34;only one&#34; could refer to the row that forms *only one*
common five-letter English word.

If this is the case, Row 4 (L U D I B - BUILD)
fits this criterion best.


Solution: The row that is an anagram of a five-letter English word
is **LUDIB**, which can be rearranged to form the word **BUILD**.

While some other rows can form five-letter words (such as RECOL
from OLCRE, WHILE from HINWL, and LOWES from WESOL), the question
states that &#34;only one of the rows... is an anagram of a five-letter
English word.&#34;  Considering the commonality and unambiguous nature
of the resulting word, &#34;BUILD&#34; from &#34;LUDIB&#34; stands out as the most
likely single correct answer. Other rows could potentially form
multiple valid five-letter words, making &#34;LUDIB&#34; unique in its most
obvious and common anagram.
</code></pre><p>That&rsquo;s a pretty long answer, but it&rsquo;s correct!</p>
<p>The answer actually concatenates both the long thinking process, and at the end, the simpler answer deduced from that thinking process.
With a version of the API endpoint that distinguishes clearly both thinking and answer parts, and with an update to the LangChain4j framework,
we&rsquo;ll make it configurable to return or not the thinking steps, in case you want to just return the final answer.</p>
<h2 id="thinking-about-it">Thinking about it&hellip;</h2>
<p>This is very interesting to see the thinking process the model follows when reasoning about the problem.
Compared to some competing model which hides its thoughts, Gemini shares everything it went through.
And it does it faster!</p>
<p>You would likely not use a reasoning model for each and every question you could ask an LLM, obviously.
However, for solving advanced problems that require a deeper thought process, this is definitely the way to go!</p>
<p>And of course, I&rsquo;m happy that <a href="https://docs.langchain4j.dev/">LangChain4j</a> lets me play with this new model out of the box!
If you don&rsquo;t feel like coding right away in Java, you can also play with the model in <a href="https://aistudio.google.com/app/prompts/new_chat">Google AI Studio</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Detecting objects with Gemini 2.0 and LangChain4j</title><link>https://glaforge.dev/posts/2024/12/13/detecting-objects-with-gemini-2-and-langchain4j/</link><pubDate>Fri, 13 Dec 2024 17:54:32 +0100</pubDate><guid>https://glaforge.dev/posts/2024/12/13/detecting-objects-with-gemini-2-and-langchain4j/</guid><description>&lt;p>Hot on the heels of the &lt;a href="https://blog.google/technology/google-deepmind/google-gemini-ai-update-december-2024/">announcement of Gemini 2.0&lt;/a>,
I played with the new experimental model both from within &lt;a href="https://aistudio.google.com/app/prompts/new_chat">Google AI Studio&lt;/a>,
and with &lt;a href="https://docs.langchain4j.dev/">LangChain4j&lt;/a>.&lt;/p>
&lt;p>Google released Gemini 2.0 Flash, with new modalities, including interleaving images, audio, text, video, both in input and output.
Even a live bidirectional speech-to-speech mode, which is really exciting!&lt;/p>
&lt;p>When experimenting with AI Studio, what attracted my attention was AI Studio&amp;rsquo;s new &lt;a href="https://aistudio.google.com/starter-apps">starter apps&lt;/a> section.
There are 3 examples (including links to Github projects showing how they were implemented):&lt;/p></description><content:encoded>
<![CDATA[<p>Hot on the heels of the <a href="https://blog.google/technology/google-deepmind/google-gemini-ai-update-december-2024/">announcement of Gemini 2.0</a>,
I played with the new experimental model both from within <a href="https://aistudio.google.com/app/prompts/new_chat">Google AI Studio</a>,
and with <a href="https://docs.langchain4j.dev/">LangChain4j</a>.</p>
<p>Google released Gemini 2.0 Flash, with new modalities, including interleaving images, audio, text, video, both in input and output.
Even a live bidirectional speech-to-speech mode, which is really exciting!</p>
<p>When experimenting with AI Studio, what attracted my attention was AI Studio&rsquo;s new <a href="https://aistudio.google.com/starter-apps">starter apps</a> section.
There are 3 examples (including links to Github projects showing how they were implemented):</p>
<ul>
<li><strong>spatial understanding</strong> — get Gemini to recognize objects in pictures, and give you bounding boxes for those objects</li>
<li><strong>video analyzer</strong> — to summarize, describe scenes, extract texts and objects from videos</li>
<li><strong>map explorer</strong> — an integration with Google Maps to explore the world</li>
</ul>
<p>The first one, on detecting objects, reminded me of an old demo of mine I had developed with Gemini 1.0 Pro Vision to recognise the cards of the Skyjo card game
(a fun little card game I&rsquo;ve been playing a lot with my youngest daughter):</p>
<p><figure>
  <a href="#img-f1f4c9f7634712cce6c90857fb574fd9">
    <img src="/img/gemini/skyjo-ai-studio.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-f1f4c9f7634712cce6c90857fb574fd9">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/skyjo-ai-studio.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>If you look at the screenshot above, you&rsquo;ll see some prompt suggestions to get bounding boxes around detected objects.
You&rsquo;ll notice that the model seems pretty capable at recnogising the numbers on those cards.
And with some bits of prompt engineering, it ignores cards facing down (attribute a value of 0 for those cards).
In the end, you can sum up all the points, and have the current score for your cards.</p>
<p>Back in the day, Gemini 1.0 was making quite a few mistakes when detecting and recognising the values of the cards,
in particular when the cards were tilted, or upside down.
But Gemini 2.0 Flash has greatly improved, and is much more capable.</p>
<p>So I decided to see:</p>
<ul>
<li>if LangChain4j works well with Gemini 2.0 Flash,</li>
<li>and if I can craft a prompt that detects my cards flawlessly.</li>
</ul>
<p>And I&rsquo;m glad to report that for all the photos I had taken of my games (14 pictures), I managed to score a 100% score of recognition.
Of course, LangChain4j is happy to call Gemini 2 without a problem (although we&rsquo;ll have to update the framework with the new modalities when a Java SDK is made available)</p>
<h2 id="lets-code">Let&rsquo;s code!</h2>
<p>I&rsquo;ll skip some of the boilerplate code to iterate over all my test pictures, properly labeled with the card values.
But you can have a look at this <a href="https://gist.github.com/glaforge/d6e845c673a5441823efc800d2d6bbf6">gist</a> with all the code.</p>
<p>First, let&rsquo;s create some Java <code>record</code>s to represent the cards, their bounding box, and number labels:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">Card</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>label,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>BoundingBox<span style="color:#bbb"> </span>boundingBox<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">BoundingBox</span>(<span style="color:#902000">int</span><span style="color:#bbb"> </span>x1,<span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>y1,<span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>x2,<span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>y2)<span style="color:#bbb"> </span>{}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>We&rsquo;ll use GSON for marshalling/unmarshalling those card details.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-2.0-flash-exp&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseMimeType</span>(<span style="color:#4070a0">&#34;application/json&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseSchema</span>(SchemaHelper.<span style="color:#4070a0">fromClass</span>(Card<span style="color:#666">[]</span>.<span style="color:#4070a0">class</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">temperature</span>(0.<span style="color:#4070a0">1f</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Notice that we&rsquo;re using the new model: <code>gemini-2.0-flash-exp</code> (it&rsquo;s labeled <em>experimental</em> for now).
And also pay attention to the response MIME type, which is JSON, and the fact we&rsquo;re defining a response schema:
We instruct Gemini to return a valid JSON object whose schema corresponds to the <code>record</code>s we&rsquo;ve just defined.</p>
<p>Next, let&rsquo;s load all the cards pictures and details (our sample dataset):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>cardsExamples<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>processImageFiles<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Path.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;skyjo-counter/samples&#34;</span>));<span style="color:#bbb">
</span></span></span></code></pre></div><p>Now we can iterate over all the cards, to check that Gemini 2 recognises all of them:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(CardsExample<span style="color:#bbb"> </span>example<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>cardsExamples)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;File: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>example.<span style="color:#4070a0">imageFile</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Let&rsquo;s look at the convoluted prompt I came up with to ensure to recognise all my sample pictures:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>model.<span style="color:#4070a0">generate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>SystemMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      Detect playing cards with numbers, with no more than 12 items.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      Output a JSON list of cards, where each entry contains the 2D
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      bounding box in `boundingBox` and the `label` is the big number
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      displayed in the center of the card.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      If you see the text &#34;SKYJO&#34; on the card, use 0 as the label
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      in `label`.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      Ignore the small numbers in the corners of the cards.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      Ignore cards with text written on them.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      Be careful when reading the numbers, as sometimes some cards
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      are tilted, cut, or upside down.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      &#34;&#34;&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>ImageContent.<span style="color:#4070a0">from</span>(example.<span style="color:#4070a0">imageFile</span>().<span style="color:#4070a0">toUri</span>()),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>TextContent.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Detect the cards of this image.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>));<span style="color:#bbb">
</span></span></span></code></pre></div><p>We give Gemini some system instructions to pay attention to the (maximum 12) cards numbers,
to return bounding boxes around the detected cards, and to give the big number at the center of the cards as the label.
There are some extra instructions for cards upside down, to ignore the small numbers in the corners, or to pay attention to the fact some cards may be cut, tilted, etc.
This prompt may not necessarily perfect, but at least it worked for all my pictures!</p>
<p>Then, as user message, we pass both the picture, and the request to detect the cards in the picture.</p>
<p>Last step, let&rsquo;s parse the JSON returned structure with GSON
(I could have used LangChain4j&rsquo;s <code>AiServices</code> for a cleaner and more type-safe approach),
and we&rsquo;re counting the points.
If the sum of points isn&rsquo;t correct, we display the cards that have been recognised, for troubleshooting purpose.</p>
<p>Let&rsquo;s check the output:</p>
<pre tabindex="0"><code>File: skyjo-counter/samples/-1 -1 -2 0 3 0 4.jpg
 ==&gt; Your points: 3
File: skyjo-counter/samples/1 4 1 -1 3 0 0 3 3 3.jpg
 ==&gt; Your points: 17
File: skyjo-counter/samples/3 9 3 4 5 2 4 5.jpg
 ==&gt; Your points: 35
File: skyjo-counter/samples/3 5 2 4 5.jpg
 ==&gt; Your points: 19
File: skyjo-counter/samples/-1 4.jpg
 ==&gt; Your points: 3
File: skyjo-counter/samples/1 0 2.jpg
 ==&gt; Your points: 3
File: skyjo-counter/samples/1 0 3 4 0 3 1 -1 2.jpg
 ==&gt; Your points: 13
File: skyjo-counter/samples/4 4 1 2 0 2 1 2 3.jpg
 ==&gt; Your points: 19
File: skyjo-counter/samples/0 -1 -1 -2 0 0 0 0 -1.jpg
 ==&gt; Your points: -5
File: skyjo-counter/samples/4 1 -2 2 4 2 3 3 0 5.jpg
 ==&gt; Your points: 22
File: skyjo-counter/samples/4 3 0 -2 -1 -1 2 1 3.jpg
 ==&gt; Your points: 9
File: skyjo-counter/samples/6 1 2 6 1 3.jpg
 ==&gt; Your points: 19
File: skyjo-counter/samples/3 3 5 2 5.jpg
 ==&gt; Your points: 18
File: skyjo-counter/samples/1 -2 5 2 -1 8 0.jpg
 ==&gt; Your points: 13
</code></pre><p>The picture file names contain the values of the cards, so it was easy to check for the ground truth!
And if we sum up those numbers, we should come up with the same number of points.</p>
<h2 id="now-what">Now what?</h2>
<p>Well, first of all, I&rsquo;m happy that LangChain4j works with Gemini 2.0!
Secondly, that the quality of object detection keeps on progressing nicely.
Thirdly, I might have to update my old demo, to make it a PWA app that could run on mobile, so that I don&rsquo;t have to count the sum of the card numbers in my head, because I&rsquo;m lazy!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Semantic code search for Programming Idioms with LangChain4j and Vertex AI embedding models</title><link>https://glaforge.dev/posts/2024/12/02/semantic-code-search-for-programming-idioms-with-langchain4j-and-vertex-ai-embedding-models/</link><pubDate>Mon, 02 Dec 2024 14:42:02 +0100</pubDate><guid>https://glaforge.dev/posts/2024/12/02/semantic-code-search-for-programming-idioms-with-langchain4j-and-vertex-ai-embedding-models/</guid><description>&lt;p>&lt;em>By Guillaume Laforge &amp;amp; Valentin Deleplace&lt;/em>&lt;/p>
&lt;p>The &lt;a href="https://programming-idioms.org/coverage">Programming Idioms&lt;/a> community website created by &lt;a href="https://www.linkedin.com/in/deleplacevalentin/">Valentin&lt;/a> lets developers share typical implementations in various programming languages for usual tasks like printing the famous “Hello World!” message, counting the characters in a string, sorting collections, or formatting dates, to name a few. And many more: there are currently 350 idioms, covering 32 programming languages. It’s a nice way to discover how various languages implement such common tasks!&lt;/p></description><content:encoded>
<![CDATA[<p><em>By Guillaume Laforge &amp; Valentin Deleplace</em></p>
<p>The <a href="https://programming-idioms.org/coverage">Programming Idioms</a> community website created by <a href="https://www.linkedin.com/in/deleplacevalentin/">Valentin</a> lets developers share typical implementations in various programming languages for usual tasks like printing the famous “Hello World!” message, counting the characters in a string, sorting collections, or formatting dates, to name a few. And many more: there are currently 350 idioms, covering 32 programming languages. It’s a nice way to discover how various languages implement such common tasks!</p>
<p>The website features a typical keyword-based search feature, which is able to search through idiom descriptions, source code, comments, and tags. However, we (Guillaume &amp; Valentin) were curious to see if we could enhance the search with a more semantic focus, taking advantage of Vertex AI <strong>embedding models</strong>, and their ability to <strong>search through code from natural language queries</strong>. With a semantic search, you’re not limited to results that match some keywords from a query, but you’ll get results even when using synonyms, or descriptions of what the code is doing.</p>
<p>Embedding models take a string in input, and generate a multidimensional floating point vector representation of that string. What’s interesting with those vectors is that input strings whose vectors are close to each other (for instance via a <a href="https://en.wikipedia.org/wiki/Cosine_similarity">cosine similarity</a> calculation) are generally close to each other semantically speaking as well. This is why you can create <strong>semantic searches</strong>: you can search for semantically similar strings, even if they don’t share the same keywords and use synonyms instead. You can explore Guillaume’s article “<a href="https://glaforge.dev/posts/2024/07/02/the-power-of-embeddings-how-numbers-unlock-the-meaning-of-data/">The power of embeddings: How numbers unlock the meaning of data</a>” to learn more about embedding models.</p>
<p>In the code shown in this article, we’ll be coding in Java, and we will be using the <a href="https://docs.langchain4j.dev/">LangChain4j</a> open source framework. You can view the <a href="https://gist.github.com/glaforge/4e45fa4222dd803d6d8bbf2b9335e90d">full source code in this gist</a>, and below, we’ll highlight the key elements of this program.</p>
<p>We’ll be using the latest version of Google Cloud Vertex AI embedding models: <code>text-embedding-005</code>. Why is it important? Because this new version supports a new <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-text-embeddings">task type</a>: <code>CODE_RETRIEVAL_QUERY</code>.</p>
<p>With this embedding model, there are different task types that optimize the embedding of text for different purposes, like for document retrieval, question &amp; answering, fact verification… and now for code retrieval queries. <strong>With this code retrieval query task type, you can search for code snippets with natural language</strong>! This will come in handy for us when we want to do natural language semantic search throughout our programming idioms!</p>
<p>Before implementing our smart code search, if you want to learn more about the new task types of our embedding models, please go check this video:</p>
<p><em>New &ldquo;task type&rdquo; embedding from the DeepMind team improves RAG search quality</em>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/BgfSCTdlvAA?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>
</p>
<h2 id="lets-collect-the-idioms">Let’s collect the idioms</h2>
<p>The <a href="https://programming-idioms.org/">Programming Idioms</a> website exposes a simple REST API. An endpoint allows you to get all the idioms in one HTTP GET call, but you can also access individual idioms via another GET request:</p>
<ul>
<li><a href="https://programming-idioms.org/api/idioms/all">https://programming-idioms.org/api/idioms/all</a> — Lists all the idioms</li>
<li><a href="https://programming-idioms.org/api/idiom/202">https://programming-idioms.org/api/idiom/202</a> — A single idiom identified by its ID</li>
</ul>
<p>Idioms contain various fields like their title, description, keywords, and provide one or more implementations in various programming languages.</p>
<p>For example, the “Sum of squares” idiom starts like this:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="">Id:</span> <span style="">202,</span>
</span></span><span style="display:flex;"><span>  <span style="">OrigId:</span> <span style="">0,</span>
</span></span><span style="display:flex;"><span>  <span style="">Title:</span> <span style="color:#062873;font-weight:bold">&#34;Sum of squares&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="">LeadParagraph:</span> <span style="color:#062873;font-weight:bold">&#34;Calculate the sum of squares _s of _data, an array of floating point values.&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="">ExtraKeywords:</span> <span style="color:#062873;font-weight:bold">&#34;reduce&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="">Author:</span> <span style="color:#062873;font-weight:bold">&#34;Bart&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="">CreationDate:</span> <span style="color:#062873;font-weight:bold">&#34;2019-09-28T20:37:11.726064Z&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="">LastEditor:</span> <span style="color:#062873;font-weight:bold">&#34;programming-idioms.org&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="">EditSummary:</span> <span style="color:#062873;font-weight:bold">&#34;New Java implementation by user [reilas]&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="">LastEditedImplID:</span> <span style="">6839,</span>
</span></span><span style="display:flex;"><span>  <span style="">OriginalAttributionURL:</span> <span style="color:#062873;font-weight:bold">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="">Picture:</span> <span style="color:#062873;font-weight:bold">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="">ImageURL:</span> <span style="color:#062873;font-weight:bold">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="">ImageWidth:</span> <span style="">0,</span>
</span></span><span style="display:flex;"><span>  <span style="">ImageHeight:</span> <span style="">0,</span>
</span></span><span style="display:flex;"><span>  <span style="">ImageAlt:</span> <span style="color:#062873;font-weight:bold">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="">Version:</span> <span style="">40,</span>
</span></span><span style="display:flex;"><span>  <span style="">VersionDate:</span> <span style="color:#062873;font-weight:bold">&#34;2024-11-08T22:54:02.691646Z&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="">Implementations:</span>
</span></span><span style="display:flex;"><span>  <span style="">[</span>
</span></span><span style="display:flex;"><span>    <span style="">{</span>
</span></span><span style="display:flex;"><span>      <span style="">Id:</span> <span style="">3466,</span>
</span></span><span style="display:flex;"><span>      <span style="">OrigId:</span> <span style="">-1,</span>
</span></span><span style="display:flex;"><span>      <span style="">Author:</span> <span style="color:#062873;font-weight:bold">&#34;Bart&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="">CreationDate:</span> <span style="color:#062873;font-weight:bold">&#34;2019-09-28T20:37:11.726064Z&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="">LastEditor:</span> <span style="color:#062873;font-weight:bold">&#34;programming-idioms.org&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="">LanguageName:</span> <span style="color:#062873;font-weight:bold">&#34;Pascal&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="">CodeBlock:</span> <span style="color:#062873;font-weight:bold">&#34;var
</span></span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">  data: array of double;
</span></span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">...
</span></span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">  s := SumOfSquares(data);
</span></span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">...&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="">OriginalAttributionURL:</span> <span style="color:#062873;font-weight:bold">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="">DemoURL:</span> <span style="color:#062873;font-weight:bold">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="">DocumentationURL:</span> <span style="color:#062873;font-weight:bold">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="">AuthorComment:</span> <span style="color:#062873;font-weight:bold">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="">Version:</span> <span style="">2,</span>
</span></span><span style="display:flex;"><span>      <span style="">VersionDate:</span> <span style="color:#062873;font-weight:bold">&#34;2021-12-07T10:07:15.952746Z&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="">Rating:</span> <span style="">0,</span>
</span></span><span style="display:flex;"><span>      <span style="">Checked:</span> <span style="">false,</span>
</span></span><span style="display:flex;"><span>      <span style="">ImportsBlock:</span> <span style="color:#062873;font-weight:bold">&#34;uses math;&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="">PictureURL:</span> <span style="color:#062873;font-weight:bold">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="">Protected:</span> <span style="">false</span>
</span></span><span style="display:flex;"><span>    }<span style="">,</span>
</span></span><span style="display:flex;"><span>    <span style="">…</span>
</span></span><span style="display:flex;"><span>  <span style="">]</span>
</span></span><span style="display:flex;"><span><span style="">}</span>
</span></span></code></pre></div><p>What’s interesting for us, for a semantic code search engine, are the following idiom fields:</p>
<ul>
<li><code>Id</code> — the unique ID of the idiom</li>
<li><code>Title</code> — that describes the idiom in a short way</li>
<li><code>LeadParagraph</code> — which is a more detailed definition of the idiom</li>
<li>ExtraKeywords — words related to the idiom, for search</li>
</ul>
<p>And for the implementations, the fields:</p>
<ul>
<li><code>Id</code> — the unique ID of the idiom implementation</li>
<li><code>CodeBlock</code> — which contains the source code of the implemented idiom</li>
<li><code>LanguageName</code> — which says which programming language was used for that implementation</li>
<li>AuthorComment — a small explanation about the implementation</li>
</ul>
<p>We can represent those two notions, idiom &amp; implementations, as Java records:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">Idiom</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@SerializedName</span>(<span style="color:#4070a0">&#34;Id&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#902000">long</span><span style="color:#bbb"> </span>id,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@SerializedName</span>(<span style="color:#4070a0">&#34;Title&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>title,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@SerializedName</span>(<span style="color:#4070a0">&#34;LeadParagraph&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>description,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@SerializedName</span>(<span style="color:#4070a0">&#34;ExtraKeywords&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>keywords,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@SerializedName</span>(<span style="color:#4070a0">&#34;Implementations&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Implementation<span style="color:#666">[]</span><span style="color:#bbb"> </span>implementations<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">Implementation</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@SerializedName</span>(<span style="color:#4070a0">&#34;Id&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#902000">long</span><span style="color:#bbb"> </span>id,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@SerializedName</span>(<span style="color:#4070a0">&#34;LanguageName&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>language,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@SerializedName</span>(<span style="color:#4070a0">&#34;CodeBlock&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>code,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@SerializedName</span>(<span style="color:#4070a0">&#34;AuthorComment&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>comment<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>The annotations are here to map between the JSON key names and the Java record field names.</p>
<p>We load all the idioms from the website, and we create <code>TextSegment</code>s, which is the class used by <a href="https://docs.langchain4j.dev/">LangChain4j</a> to pass to the embedding model for creating vectors.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Idiom<span style="color:#666">[]</span><span style="color:#bbb"> </span>idioms<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>loadIdioms();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(Idiom<span style="color:#bbb"> </span>idiom<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>idioms)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;-&gt; &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>idiom.<span style="color:#4070a0">title</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(<span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>implementation<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>idiom.<span style="color:#4070a0">implementations</span>)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>implementation<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>idiom.<span style="color:#4070a0">implementations</span><span style="color:#666">[</span>j<span style="color:#666">]</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(implementation.<span style="color:#4070a0">code</span><span style="color:#bbb"> </span><span style="color:#666">!=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span><span style="color:#666">&amp;&amp;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">               </span><span style="color:#666">!</span>implementation.<span style="color:#4070a0">code</span>.<span style="color:#4070a0">isBlank</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>allCodeSegments.<span style="color:#4070a0">add</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>TextSegment(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>implementation.<span style="color:#4070a0">code</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Metadata()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;idiomId&#34;</span>,<span style="color:#bbb"> </span>idiom.<span style="color:#4070a0">id</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;title&#34;</span>,<span style="color:#bbb"> </span>idiom.<span style="color:#4070a0">title</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;description&#34;</span>,<span style="color:#bbb"> </span>idiom.<span style="color:#4070a0">description</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;titleAndDescription&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span>idiom.<span style="color:#4070a0">title</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>idiom.<span style="color:#4070a0">description</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;keywords&#34;</span>,<span style="color:#bbb"> </span>idiom.<span style="color:#4070a0">keywords</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;implementationId&#34;</span>,<span style="color:#bbb"> </span>implementation.<span style="color:#4070a0">id</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">put</span>(<span style="color:#4070a0">&#34;language&#34;</span>,<span style="color:#bbb"> </span>implementation.<span style="color:#4070a0">language</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Notice that we also add some metadata. Not only do we embed the code snippets, but we also add some extra information like the title, description, keywords, or programming language. This will be useful for showing the results found during the semantic search.</p>
<p>We create a metadata field that concatenates the title and description of the idiom, as this is useful meta-information that the embedding model can use when calculating the vector embeddings. The <code>text-embedding-005</code> model pays attention to that information, and this will influence the calculations and enrich the semantic context of the vector embedding.</p>
<h2 id="calculating-embedding-vectors">Calculating embedding vectors</h2>
<p>To compute those embeddings, we configure and use the <code>text-embedding-005</code> embedding model offered by Vertex AI. We define two instances of the model, with two distinct task types:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>VertexAiEmbeddingModel<span style="color:#bbb"> </span>EMBEDDING_MODEL<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>VertexAiEmbeddingModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;text-embedding-005&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">publisher</span>(<span style="color:#4070a0">&#34;google&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">taskType</span>(VertexAiEmbeddingModel.<span style="color:#4070a0">TaskType</span>.<span style="color:#4070a0">RETRIEVAL_DOCUMENT</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">titleMetadataKey</span>(<span style="color:#4070a0">&#34;titleAndDescription&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">maxSegmentsPerBatch</span>(150)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>VertexAiEmbeddingModel<span style="color:#bbb"> </span>EMBEDDING_MODEL_FOR_RETRIEVAL<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>VertexAiEmbeddingModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;text-embedding-005&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">publisher</span>(<span style="color:#4070a0">&#34;google&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">taskType</span>(VertexAiEmbeddingModel.<span style="color:#4070a0">TaskType</span>.<span style="color:#4070a0">CODE_RETRIEVAL_QUERY</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">titleMetadataKey</span>(<span style="color:#4070a0">&#34;titleAndDescription&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>We use the <code>EMBEDDING_MODEL</code> with a <code>RETRIEVAL_DOCUMENT</code> task type for the calculation of the vector embedding, but we use the <code>EMBEDDING_MODEL_FOR_RETRIEVAL</code> instance one, with a <code>CODE_RETRIEVAL_QUERY</code> task type for the retrieval.</p>
<p>The <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/task-types#benefits_of_task_types">documentation on task types</a> explains that it helps optimize the vector embedding calculation for different types of tasks. And this is what allows us to compare natural language queries like <code>&quot;calculating string length&quot;</code> with the actual code that computes the length of a string. Task types put the questions and answers closer in the embedding space.</p>
<p>We calculate all the embeddings in batch with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>InMemoryEmbeddingStore<span style="color:#666">&lt;</span>TextSegment<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>embeddingStore<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>InMemoryEmbeddingStore<span style="color:#666">&lt;&gt;</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>List<span style="color:#666">&lt;</span>Embedding<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>allEmbeddings<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>EMBEDDING_MODEL.<span style="color:#4070a0">embedAll</span>(allCodeSegments).<span style="color:#4070a0">content</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>embeddingStore.<span style="color:#4070a0">addAll</span>(allEmbeddings,<span style="color:#bbb"> </span>allCodeSegments);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>embeddingStore.<span style="color:#4070a0">serializeToFile</span>(filePath);<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="embedding-the-query-and-searching">Embedding the query and searching</h2>
<p>With vector databases, when doing a search, we compare a vector embedding of what we’re searching for, with all the vector embeddings stored. So now that we have all our code snippets embedded, we need to compare an embedding of a user query to all those snippets. The in-memory embedding store can calculate cosine similarities between vectors for us.</p>
<p>Simplifying the code from the <a href="https://gist.github.com/glaforge/4e45fa4222dd803d6d8bbf2b9335e90d">gist</a> a little, what we do here is to calculate the embedding for the user query, and prepare an embedding search request:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Embedding<span style="color:#bbb"> </span>queryEmbedding<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>EMBEDDING_MODEL_FOR_RETRIEVAL.<span style="color:#4070a0">embed</span>(question)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">content</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>searchRequestBuilder<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>EmbeddingSearchRequest.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">maxResults</span>(5)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">minScore</span>(0.<span style="color:#4070a0">8</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">queryEmbedding</span>(queryEmbedding)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>EmbeddingSearchResult<span style="color:#666">&lt;</span>TextSegment<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>searchResult<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>embeddingStore.<span style="color:#4070a0">search</span>(searchRequest);<span style="color:#bbb">
</span></span></span></code></pre></div><p>We chose to return only the 5 best search results, whose minimal score is above 0.8 (the score is a value between 0 and 1, with 1 being the highest). Then, we can iterate over the hits, and display the results for this search with some formatting:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>searchResult.<span style="color:#4070a0">matches</span>().<span style="color:#4070a0">forEach</span>(match<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>TextSegment<span style="color:#bbb"> </span>matchedSegment<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>match.<span style="color:#4070a0">embedded</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">format</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            ——— %s ——— (score: %4.5f) —————————
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Title: %s
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Description: %s
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Code:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            %s
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;&#34;&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>matchedSegment.<span style="color:#4070a0">metadata</span>().<span style="color:#4070a0">getString</span>(<span style="color:#4070a0">&#34;language&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>match.<span style="color:#4070a0">score</span>(),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>matchedSegment.<span style="color:#4070a0">metadata</span>().<span style="color:#4070a0">getString</span>(<span style="color:#4070a0">&#34;title&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>matchedSegment.<span style="color:#4070a0">metadata</span>().<span style="color:#4070a0">getString</span>(<span style="color:#4070a0">&#34;description&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>matchedSegment.<span style="color:#4070a0">text</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>});<span style="color:#bbb">
</span></span></span></code></pre></div><p>We can try different queries:</p>
<ul>
<li>How can I make an HTTP POST request?</li>
<li>How to count the characters in a string?</li>
<li>How to use the LibXML parser in Perl?</li>
</ul>
<p>For example, for the first query, the top results look interesting, with some good scores:</p>
<pre tabindex="0"><code>——— Java ——— (score: 0.85341) —————————
Title: Make HTTP POST request

Description: Make a HTTP request with method POST to the URL u

Code:
String s = HttpClient.newHttpClient().send(HttpRequest.newBuilder()
                        .uri(URI.create(u))
                        .POST(HttpRequest.BodyPublishers.ofString(content))
                        .build(), HttpResponse.BodyHandlers.ofString())
                .body();

——— D ——— (score: 0.84189) —————————
Title: Make HTTP POST request

Description: Make a HTTP request with method POST to the URL u

Code:
auto response = post(u, content);

——— Go ——— (score: 0.84010) —————————
Title: Make HTTP POST request

Description: Make a HTTP request with method POST to the URL u

Code:
response, err := http.Post(u, contentType, body)

——— Go ——— (score: 0.83938) —————————
Title: Make HTTP POST request

Description: Make a HTTP request with method POST to the URL u

Code:
response, err := http.PostForm(u, formValues)

——— Lisp ——— (score: 0.83770) —————————
Title: Make HTTP POST request

Description: Make a HTTP request with method POST to the URL u

Code:
(dex:post u)
</code></pre><p>Our search implementation found the right idioms and implementations.</p>
<h2 id="restricting-the-search-with-metadata-filtering">Restricting the search with metadata filtering</h2>
<p>Now if we try to be more specific, like our question that asks explicitly to search for a specific programming language like Perl, the search would yield results in all programming languages. But the user wanted only Perl examples! Instead, to have better and more precise results, we can take advantage of LangChain4j’s <a href="https://docs.langchain4j.dev/integrations/embedding-stores/">metadata filtering</a>.</p>
<p>You remember that we added various metadata information to our embedded text segments? We included the programming language used in the code snippet in a language metadata field. With metadata filtering, we can focus the search only on a subset of vector embeddings whose language metadata field matches the programming language we’re interested in.</p>
<p>Let’s update our search query as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>searchRequestBuilder<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>EmbeddingSearchRequest.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">maxResults</span>(5)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">minScore</span>(0.<span style="color:#4070a0">8</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">queryEmbedding</span>(queryEmbedding)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">filter</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>IsEqualTo(<span style="color:#4070a0">&#34;language&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>programmingLanguageRecognised))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>We added a <code>filter()</code> method, that checks that the language is equal to some value. But then, it means we have to know up-front that the user wants results just for one specific programming language. We could have some kind of UI element that users have to fill to select the programming language. But in our search query, we had a user providing the programming language directly in that query: <code>&quot;How to use the LibXML parser in Perl?&quot;</code></p>
<p>In such a situation, we can’t rely on a UI component or CLI parameter, we have to guess the programming language requested from the query string itself. This is where Gemini can come to the rescue, with a little bit of prompting, we can ask the generative model to tell us if a programming language is present in the query, and which one.</p>
<p>First, let’s have a look at the programming languages offered by Programming Idioms:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>List<span style="color:#666">&lt;</span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>KNOWN_PROGRAMMING_LANGUAGES<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>List.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;UNKNOWN&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Go&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Rust&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Python&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Perl&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Ruby&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Java&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;JS&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;C#&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Dart&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Pascal&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;PHP&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;C++&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Haskell&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;D&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Lua&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Clojure&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Fortran&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Elixir&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Kotlin&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Erlang&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;C&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Lisp&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;VB&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Groovy&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Ada&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Scala&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Scheme&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Smalltalk&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Obj-C&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Cobol&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Prolog&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Caml&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>We added an <code>UNKNOWN</code> value, when the language is not specified or recognised.</p>
<p>Now we configure a Gemini 1.5 Flash model, specifying a response schema to restrict the model’s answer to a value contained in the language enumeration of possible programming languages:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>ChatLanguageModel<span style="color:#bbb"> </span>GEMINI_MODEL<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash-002&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">responseSchema</span>(Schema.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">setType</span>(Type.<span style="color:#4070a0">STRING</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">addAllEnum</span>(KNOWN_PROGRAMMING_LANGUAGES)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Let’s prompt Gemini to find the programming language in the user query (if present):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>String<span style="color:#bbb"> </span>programmingLanguageRecognised<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>GEMINI_MODEL.<span style="color:#4070a0">generate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>SystemMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Your role is to classify the user message to decide
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            if it is a question about a particular programming
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            language or not.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            If you don&#39;t know, or if the programming language
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            is not specified, reply with `UNKNOWN`, otherwise
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            reply with just the name of the programming
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            language recognized among the following list:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;&#34;&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>KNOWN_PROGRAMMING_LANGUAGES),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>UserMessage.<span style="color:#4070a0">from</span>(question)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>).<span style="color:#4070a0">content</span>().<span style="color:#4070a0">text</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Gemini will either reply with <code>UNKNOWN</code> if no programming language was mentioned, or with the language it has recognized.</p>
<p>Now, when making a search for an idiom in a particular language, only implementations in that language are returned, giving much better results, in line with the expectations of the user.</p>
<h2 id="possible-further-improvements">Possible further improvements</h2>
<p>Where can we go from there? We can make the search a little bit snappier, or further enhance the quality of the search results.</p>
<p>Let’s talk first about the search speed. Searching through the in-memory vector database is pretty fast, and only requires a couple dozen milliseconds. After all, it’s all in memory, and there’s not millions of records in the database. But what takes more time are the round trips to the cloud hosted embedding models and for the generative model calls.</p>
<p>Depending on the cloud region you use, and from where you call the program, an embedding request can take up to a second and a half, and the Gemini call less than a second. So making a request to Gemini to guess the programming language, then calling the embedding model to embed the query for comparison with the in-memory database, would be roughly two and a half seconds long if done serially. Since both operations are unrelated, we can call them in parallel using an executor service with two threads:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>List<span style="color:#666">&lt;</span>Future<span style="color:#666">&lt;</span>Object<span style="color:#666">&gt;&gt;</span><span style="color:#bbb"> </span>futures;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(<span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>executorService<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Executors.<span style="color:#4070a0">newFixedThreadPool</span>(2))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>futures<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>executorService.<span style="color:#4070a0">invokeAll</span>(List.<span style="color:#4070a0">of</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>()<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>recognizeProgrammingLanguage(question),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>()<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>embedQuery(question)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>programmingLanguageRecognised<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>(String)<span style="color:#bbb"> </span>futures.<span style="color:#4070a0">get</span>(0).<span style="color:#4070a0">get</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Embedding<span style="color:#bbb"> </span>queryEmbedding<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>(Embedding)<span style="color:#bbb"> </span>futures.<span style="color:#4070a0">get</span>(1).<span style="color:#4070a0">get</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>With this trick, the embedding and programming language guessing takes as much time as the longest of both tasks. Usually, it seems the embedding is the longest. So we shave a second of wait time for the user. It’s a win!</p>
<p>The other aspect we could improve further is the quality of search results. We already improved it by applying two techniques: using a code retrieval task type with our embedding model, and also the programming language filtering to avoid returning languages the user isn’t interested in.</p>
<p>However, there’s another approach we haven’t explored (this could be the topic for another article) which is to combine the existing keyword-based search provided by the Programming Idioms website, with our semantic search. This is what is called <strong>hybrid search</strong>: combining the results of two or more searches, to give better results, applying techniques like <a href="https://medium.com/@devalshah1619/mathematical-intuition-behind-reciprocal-rank-fusion-rrf-explained-in-2-mins-002df0cc5e2a">Reciprocal Rank Fusion</a> to merge results.</p>
<p>Embedding and generative models understand text pretty well, but can struggle with acronyms, product names, etc, that they haven’t seen much (if at all) in their training set. But keyword-based searches excel at that. So by combining the best of both worlds, our little website search box could tackle more queries, and give the best answers to our users.</p>
<h2 id="summary">Summary</h2>
<p>This article explored semantic code search for programming idioms using Vertex AI <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings">embedding models</a> and the <a href="https://docs.langchain4j.dev/">LangChain4j</a> framework. We aimed to enable natural language queries for code examples, going beyond keyword-based searches. Key learnings included:</p>
<ul>
<li><strong>Embedding models</strong> represented text as multidimensional vectors, capturing semantic similarities.</li>
<li><strong>Vertex AI&rsquo;s text-embedding-005</strong> model, particularly the <code>CODE_RETRIEVAL_QUERY</code> task type, was optimized for code-related searches.</li>
<li><strong>LangChain4j</strong> provided a framework for building LLM applications in Java.</li>
<li><strong>Gemini</strong>, a generative AI model, could be used to infer the programming language from a user&rsquo;s query, improving search accuracy.</li>
<li><strong>Parallel processing</strong> enhanced search speed by concurrently executing embedding and language recognition tasks.</li>
<li><strong>Metadata filtering</strong> allowed for more precise searches based on attributes like the programming language name.</li>
<li><strong>Hybrid search</strong>, combining semantic and keyword-based approaches, could further improve search quality.</li>
</ul>
<p>Overall, the article demonstrated how we could build a fast and intelligent programming idiom search engine that understands natural language queries and retrieves contextually relevant code examples.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Redacting sensitive information when using Generative AI models</title><link>https://glaforge.dev/posts/2024/11/25/redacting-sensitive-information-when-using-generative-ai-models/</link><pubDate>Mon, 25 Nov 2024 09:24:50 +0100</pubDate><guid>https://glaforge.dev/posts/2024/11/25/redacting-sensitive-information-when-using-generative-ai-models/</guid><description>&lt;p>As we are making our apps smarter with the help of Large Language Models, we must keep in mind that we are often dealing with potentially sensitive information coming from our users. In particular, in the context of chatbots, our application users have the ability to input any text in the conversation.&lt;/p>
&lt;p>Personally Identifiable Information (PII) should be dealt with the highest level of attention, because we care about our users, we don&amp;rsquo;t want to leak their personal details, and we must comply with all sorts of laws or regulations. In a word, we are &lt;a href="https://cloud.google.com/responsible-ai">responsible AI&lt;/a> developers.&lt;/p></description><content:encoded>
<![CDATA[<p>As we are making our apps smarter with the help of Large Language Models, we must keep in mind that we are often dealing with potentially sensitive information coming from our users. In particular, in the context of chatbots, our application users have the ability to input any text in the conversation.</p>
<p>Personally Identifiable Information (PII) should be dealt with the highest level of attention, because we care about our users, we don&rsquo;t want to leak their personal details, and we must comply with all sorts of laws or regulations. In a word, we are <a href="https://cloud.google.com/responsible-ai">responsible AI</a> developers.</p>
<p>In this article, we&rsquo;ll learn about the Google Cloud <a href="https://cloud.google.com/security/products/dlp">Data Loss Prevention</a> (DLP) API. It&rsquo;s a very powerful and rich service, which allows you to identify, classify, filter, redact any PII like names, passport numbers, bank account numbers, and more.</p>
<p>Today, with DLP, our goal is to redact the PII information sent by our user, before sending the user&rsquo;s message to our LLM.</p>
<p>In a nutshell (in pseudo-code), instead of doing:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>String<span style="color:#bbb"> </span>userMessage<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;...&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(userMessage);<span style="color:#bbb">
</span></span></span></code></pre></div><p>We want to add an instruction in the middle to redact the personally identifiable information before sending it to the LLM, so we need to to insert a method in between:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>String<span style="color:#bbb"> </span>userMessage<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;...&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>redactedMessage<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>redact(userMessage);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(redactedMessage);<span style="color:#bbb">
</span></span></span></code></pre></div><p>We&rsquo;ll simply redact the user message, but remember that there are other areas where you can apply good practices when handling user information. For example, when you store data, when you log interactions, etc.</p>
<h2 id="meet-our-user">Meet our user!</h2>
<p>Our user, let&rsquo;s call her Alicia, is a bit talkative, and shares way too much information that she should. Let&rsquo;s imagine that she is travelling, and lost her wallet, and needs some money to be wired in a rush. Maybe she could send a message to our travel application that looks as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>String<span style="color:#bbb"> </span>userMessage<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    My name is Alicia Bob.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    My number is +33612345678, can you call me please?
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Please wire some $$$ on FR7630001007941234567890185
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    You can check my passport if needed, it&#39;s 78TH67845.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>;<span style="color:#bbb">
</span></span></span></code></pre></div><p>Woh! In one message she gave her name, her phone number, her bank account (IBAN), and even her passport number! But our application doesn&rsquo;t necessarily need all those details!</p>
<p>In our code, we&rsquo;re sending that information to our Gemini model, using <a href="https://docs.langchain4j.dev/">LangChain4j</a>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash-002&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>redactedMessage<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>redact(userMessage);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(redactedMessage);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(redactedMessage);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Our mission, if we accept it, is to implement the <code>redact()</code> method that will remove all the PII information from that request.</p>
<h2 id="redacting-this-redacted-message">Redacting this [REDACTED] message!</h2>
<p>First, let&rsquo;s have a look at all the code of our <code>redact()</code> method, and we&rsquo;ll explain bits and pieces further down.
You can also look at this <a href="https://gist.github.com/glaforge/c7d7188aa3ff01a0f691b1e474ec0260">gist</a> on Github with all the code as well.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">redact</span>(String<span style="color:#bbb"> </span>userMessage)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(<span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>dlp<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>DlpServiceClient.<span style="color:#4070a0">create</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>item<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ContentItem.<span style="color:#4070a0">newBuilder</span>().<span style="color:#4070a0">setValue</span>(userMessage).<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>inspectConfigbuilder<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>InspectConfig.<span style="color:#4070a0">newBuilder</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>redactConfig<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>DeidentifyConfig.<span style="color:#4070a0">newBuilder</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>infoTypeTransfBuilder<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>InfoTypeTransformations.<span style="color:#4070a0">newBuilder</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Stream.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;PERSON_NAME&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;PHONE_NUMBER&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;PASSPORT&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;IBAN_CODE&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">forEach</span>(toRedact<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>infoType<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>InfoType.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">setName</span>(toRedact)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>inspectConfigbuilder.<span style="color:#4070a0">addInfoTypes</span>(infoType);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>replaceValueConfig<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>ReplaceValueConfig.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span>.<span style="color:#4070a0">setNewValue</span>(Value.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span>.<span style="color:#4070a0">setStringValue</span>(<span style="color:#4070a0">&#34;[&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>toRedact<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;]&#34;</span>).<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>primitiveTransformation<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>PrimitiveTransformation.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span>.<span style="color:#4070a0">setReplaceConfig</span>(replaceValueConfig).<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>infoTypeTransformation<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>InfoTypeTransformations.<span style="color:#4070a0">InfoTypeTransformation</span>.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span>.<span style="color:#4070a0">addInfoTypes</span>(infoType)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span>.<span style="color:#4070a0">setPrimitiveTransformation</span>(primitiveTransformation)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>infoTypeTransfBuilder<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">addTransformations</span>(infoTypeTransformation);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>});<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>redactConfig.<span style="color:#4070a0">setInfoTypeTransformations</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>infoTypeTransfBuilder);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>DeidentifyContentRequest<span style="color:#bbb"> </span>request<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>DeidentifyContentRequest.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setParent</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>LocationName.<span style="color:#4070a0">of</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_ID&#34;</span>),<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;global&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">toString</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setItem</span>(item)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setDeidentifyConfig</span>(redactConfig)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setInspectConfig</span>(inspectConfigbuilder)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>DeidentifyContentResponse<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>dlp.<span style="color:#4070a0">deidentifyContent</span>(request);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>response.<span style="color:#4070a0">getItem</span>().<span style="color:#4070a0">getValue</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">catch</span><span style="color:#bbb"> </span>(IOException<span style="color:#bbb"> </span>e)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">throw</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>RuntimeException(<span style="color:#4070a0">&#34;Failed to redact message.&#34;</span>,<span style="color:#bbb"> </span>e);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>As you can see, the DLP API is quite a bit verbose, but it&rsquo;s really super powerful, and is capable of more than just redacting PII information.</p>
<p>First of all, we need to create a client for the DLP service (which is <code>AutoCloseable</code>, hence the <code>try</code> with resources pattern):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(<span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>dlp<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>DlpServiceClient.<span style="color:#4070a0">create</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb"> </span>...<span style="color:#bbb"> </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">catch</span><span style="color:#bbb"> </span>(...)<span style="color:#bbb"> </span>{...}<span style="color:#bbb">
</span></span></span></code></pre></div><p>We create a <code>ContentItem</code> from our user message:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>item<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ContentItem.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">setValue</span>(userMessage)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Then we&rsquo;ll create some <code>InfoType</code>s which represent the different kinds of identifiable information we&rsquo;re interested in:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Stream.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;PERSON_NAME&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;PHONE_NUMBER&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;PASSPORT&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;IBAN_CODE&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">forEach</span>(toRedact<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>infoType<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>InfoType.<span style="color:#4070a0">newBuilder</span>().<span style="color:#4070a0">setName</span>(toRedact).<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>inspectConfigbuilder.<span style="color:#4070a0">addInfoTypes</span>(infoType);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Here, we care only for the person&rsquo;s name, phone number, passport, and IBAN codes. But there are a ton of <a href="https://cloud.google.com/sensitive-data-protection/docs/infotypes-reference">other details we can redact</a>.</p>
<p>The next few instructions will associate a text transformation rule to transform the PII information into some redacted format. We could have used just something like <code>[REDACTED]</code> but we are going to reuse the name of the info type: <code>[PERSON_NAME]</code>, <code>[PHONE_NUMBER]</code>, <code>[PASSPORT]</code>, and <code>[IBAN_CODE]</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>replaceValueConfig<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>ReplaceValueConfig.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setNewValue</span>(Value.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">setStringValue</span>(<span style="color:#4070a0">&#34;[&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>toRedact<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;]&#34;</span>).<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>primitiveTransformation<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>PrimitiveTransformation.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setReplaceConfig</span>(replaceValueConfig).<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>infoTypeTransformation<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>InfoTypeTransformations.<span style="color:#4070a0">InfoTypeTransformation</span>.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">addInfoTypes</span>(infoType)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setPrimitiveTransformation</span>(primitiveTransformation)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>We add all those text transformations to the information type transformation builder, and then it&rsquo;s time to actually make the request to the DLP service:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>DeidentifyContentRequest<span style="color:#bbb"> </span>request<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>DeidentifyContentRequest.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setParent</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>LocationName.<span style="color:#4070a0">of</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_ID&#34;</span>),<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;global&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span>.<span style="color:#4070a0">toString</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setItem</span>(item)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setDeidentifyConfig</span>(redactConfig)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setInspectConfig</span>(inspectConfigbuilder)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>DeidentifyContentResponse<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>dlp.<span style="color:#4070a0">deidentifyContent</span>(request);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>response.<span style="color:#4070a0">getItem</span>().<span style="color:#4070a0">getValue</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>We wire everything together by creating a <code>DeidentifyContentRequest</code> instance with our user message (the item) and all PII identification and transformation configuration. We configured the DLP service by passing our Google Cloud project ID, after having <a href="https://console.cloud.google.com/apis/library/dlp.googleapis.com">enabled the API</a>. We call the DLP service with <code>dlp.deidentifyContent(request)</code> and finally we can get the redacted value with <code>response.getItem().getValue()</code>.</p>
<p>So what does our original user message look like now, once redaction is applied? Let&rsquo;s see:</p>
<pre tabindex="0"><code>My name is [PERSON_NAME] [PERSON_NAME].
My number is [PHONE_NUMBER], can you call me please?
Please wire some $$$ on [IBAN_CODE]
You can check my passport if needed, it&#39;s [PASSPORT].
</code></pre><p>No more personally identifiable information left!</p>
<h2 id="summary">Summary</h2>
<p>Our user&rsquo;s trust is one of the most important things we must care about. Not only for compliance purposes but also simply because it&rsquo;s the right thing to do. There are so many hackers out there trying to get access to such information, for nefarious reasons. Let&rsquo;s not offer them an extra chance to harm our users.</p>
<p>In this article and sample code, we&rsquo;ve seen that the <a href="https://cloud.google.com/security/products/dlp?hl=en">Google Cloud DLP API</a> is able to redact information, but it can be used in a myriad of ways, for example to analyze data at rest as well, or you can deidentify / reidentify information as well. Be sure to check out what this service is capable of doing. We focused on just a few PII details, but DLP supports a <a href="https://cloud.google.com/sensitive-data-protection/docs/infotypes-reference">huge number of identifiable information</a>.</p>
<p>There&rsquo;s a big <a href="https://cloud.google.com/sensitive-data-protection/docs/samples/?hl=en">list of snippets</a> of code that you can have a look at to see what you can do with the DLP API. There are <a href="https://cloud.google.com/sensitive-data-protection/docs/libraries">SDKs</a> for various programming languages, if you use another language than Java. And check out the <a href="https://cloud.google.com/sensitive-data-protection/docs/sensitive-data-protection-overview">documentation</a>!</p>
<p>You can apply this technique to filter user input before sending it to a generative model, but you can also apply it in output as well, when/if you log user messages, or store data in databases or other places.</p>
<p>And remember, be mindful of your user&rsquo;s data!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Data extraction: The many ways to get LLMs to spit JSON content</title><link>https://glaforge.dev/posts/2024/11/18/data-extraction-the-many-ways-to-get-llms-to-spit-json-content/</link><pubDate>Mon, 18 Nov 2024 09:47:28 +0100</pubDate><guid>https://glaforge.dev/posts/2024/11/18/data-extraction-the-many-ways-to-get-llms-to-spit-json-content/</guid><description>&lt;p>Data extraction from unstructured text is a very important task where LLMs shine, as they understand human languages well. Rumor has it that 80% of the worldwide knowledge and data comes in the form of unstructured text (vs 20% for data stored in databases, spreadsheets, JSON/XML, etc.) Let’s see how we can get access to that trove of information thanks to LLMs.&lt;/p>
&lt;p>In this article, we’ll have a look at different techniques to make LLMs generate JSON output and extract data from text. This applies to most LLMs and frameworks, but for illustration purposes, we’ll use &lt;a href="https://deepmind.google/technologies/gemini/">Gemini&lt;/a> and &lt;a href="https://docs.langchain4j.dev/">LangChain4j&lt;/a> in Java.&lt;/p></description><content:encoded>
<![CDATA[<p>Data extraction from unstructured text is a very important task where LLMs shine, as they understand human languages well. Rumor has it that 80% of the worldwide knowledge and data comes in the form of unstructured text (vs 20% for data stored in databases, spreadsheets, JSON/XML, etc.) Let’s see how we can get access to that trove of information thanks to LLMs.</p>
<p>In this article, we’ll have a look at different techniques to make LLMs generate JSON output and extract data from text. This applies to most LLMs and frameworks, but for illustration purposes, we’ll use <a href="https://deepmind.google/technologies/gemini/">Gemini</a> and <a href="https://docs.langchain4j.dev/">LangChain4j</a> in Java.</p>
<p>We’ll explore the following approaches:</p>
<ul>
<li>prompting</li>
<li>function calling</li>
<li>structured output with a JSON mode</li>
<li>structured output with a JSON response schema</li>
</ul>
<h2 id="lets-get-started">Let’s get started</h2>
<p>Your mission, if you accept it, is to extract the name and age from the biography of a person:</p>
<pre tabindex="0"><code>Anna is a 23 year old artist based in Brooklyn, New York. She
was born and raised in the suburbs of Chicago, where she developed a
love for art at a young age. She attended the School of the Art
Institute of Chicago, where she studied painting and drawing. After
graduating, she moved to New York City to pursue her art career.
Anna&#39;s work is inspired by her personal experiences and observations
of the world around her. She often uses bright colors and bold lines
to create vibrant and energetic paintings. Her work has been exhibited
in galleries and museums in New York City and Chicago.
</code></pre><p>From that text, we want to extract the following JSON snippet:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;name&#34;</span>: <span style="color:#4070a0">&#34;Anna&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;age&#34;</span>: <span style="color:#40a070">23</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="lets-just-ask-politely">Let’s just ask politely!</h2>
<p>The first approach is to simply craft a user message, via prompting, that requests the response to be returned as JSON. A simple prompt suffice:</p>
<pre tabindex="0"><code>Return the name and age of the person described in the biography below.
Give the name and age in the form of a JSON object following this
structure: `{&#34;name&#34;: &#34;Jon Doe&#34;, &#34;age&#34;: 36}`
Only return JSON, without any explanation,
without surrounding markdown code markup.

Here is the biography:

Anna is a 23 year old artist based in Brooklyn, New York. She
was born and raised in the suburbs of Chicago, where she developed a
love for art at a young age. She attended the School of the Art
Institute of Chicago, where she studied painting and drawing. After
graduating, she moved to New York City to pursue her art career.
Anna&#39;s work is inspired by her personal experiences and observations
of the world around her. She often uses bright colors and bold lines
to create vibrant and energetic paintings. Her work has been exhibited
in galleries and museums in New York City and Chicago.

JSON:
</code></pre><p>Sometimes, LLMs don’t always follow precisely the instructions. So you have to nudge them a little bit by requesting them to really output only JSON, as sometimes they wrap their answers with messages like “Here is the name and age of the person…” or with extra Markdown code blocks. So you may have to further tweak the prompt.</p>
<p>Quick illustration with Gemini and LangChain4j:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>String<span style="color:#bbb"> </span>biography<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Anna is a 23 year old artist…&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-pro-002&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Return the name and age of the person described in the biography
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    below. Give the name and age in the form of a JSON object
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    following this structure: `{&#34;name&#34;: &#34;Jon Doe&#34;, &#34;age&#34;: 36}`
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Only return JSON, without any explanation,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    without surrounding markdown code markup.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Here is the biography:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>biography<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    JSON:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// {&#34;name&#34;: &#34;Anna&#34;, &#34;age&#34;: 23}</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>The output is a <code>String</code>, so you have to parse it with your favorite JSON parser, but the data has been successfully extracted into a JSON object.</p>
<p>Most LLMs support the notion of system instructions. Usually, LLMs obey a bit more closely to those instructions, than via user prompts. So you could also rewrite the example above by splitting the instructions inside system instructions, and put only the biography in the user prompt.</p>
<h2 id="function-calling-to-the-rescue">Function calling to the rescue!</h2>
<p>Before the advent of JSON modes and response schemas (that we’ll review in the next sections) a more certain way to get JSON outputs was to take advantage of function calling. You have to encourage the LLM to request a function call to extract the information. Here’s the trick.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;name&#34;</span>: <span style="color:#4070a0">&#34;extractNameAndAgeFromBiography&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;description&#34;</span>: <span style="color:#4070a0">&#34;extract the name and age of a person described in the biographical text given in input&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;parameters&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#4070a0">&#34;object&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;properties&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;name&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#4070a0">&#34;string&#34;</span>
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;age&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#4070a0">&#34;integer&#34;</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;required&#34;</span>: [<span style="color:#4070a0">&#34;name&#34;</span>, <span style="color:#4070a0">&#34;age&#34;</span>]
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>You define a function whose signature looks like <code>extractNameAndAgeFromBiography(String name, int age)</code>, following the OpenAPI specification. You should add very precise descriptions for the function and its arguments. Here, I could have added more information about the parameters, but the names seemed self-explanatory to me. Then you can just pass the biography directly, and it should just work out of the box.</p>
<p>You can add system instructions to request the model to call that method to find the name and age of the person. But sometimes, some LLMs also allow you to force the LLM to request a call to a function.</p>
<p>What does it look like in Java with LangChain4j?</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-pro-002&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">toolCallingMode</span>(ToolCallingMode.<span style="color:#4070a0">ANY</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">allowedFunctionNames</span>(List.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;extractNameAndAgeFromBiography&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>I specified the tool calling mode: this <code>ANY</code> value instructs the model to call one of the methods defined in the allowed function names list. It is a forced call request. The model will have to request the call.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>List.<span style="color:#4070a0">of</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>SystemMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          Return the name and age of the person described by the user
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          by calling the function `extractNameAndAgeFromBiography()`
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">          and passing the name and the age of the person recognized.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>UserMessage.<span style="color:#4070a0">from</span>(biography)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>ToolSpecification.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            extract the name and age of a person described
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            in the biographical text given in input
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;extractNameAndAgeFromBiography&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">parameters</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>JsonObjectSchema.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">addStringProperty</span>(<span style="color:#4070a0">&#34;name&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">addIntegerProperty</span>(<span style="color:#4070a0">&#34;age&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">required</span>(<span style="color:#4070a0">&#34;name&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;age&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">build</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>The <code>generate()</code> call is a bit more convoluted. With forced tool calling, the system message is not mandatory, but it can help ensure all parameters are passed as arguments. Look at how we defined the contract of the function <code>extractNameAndAgeFromBiography()</code> by creating an object with a string and integer properties.</p>
<p>Now we’ll extract the function call request. We don’t look at the text content, as the model returns a tool execution request instead:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">content</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">toolExecutionRequests</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">getFirst</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">arguments</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// {&#34;name&#34;:&#34;Anna&#34;,&#34;age&#34;:23.0}</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>You can retrieve just the arguments, as a JSON string. It’s already following the JSON object structure we wished to obtain.</p>
<p>You might notice a minor annoyance here, though, which is the fact the age is not an integer, but a floating point number. I’m not entirely sure at this point why we don’t get an integer. I’ll have to dig a little deeper…</p>
<p>Let’s now have a look at the JSON mode and response schema approaches.</p>
<h2 id="json-mode-approach">JSON mode approach</h2>
<p>Some LLMs started offering the ability to request the model to output valid JSON. It’s not necessarily 100% certain that it will follow your requested format (for example, some JSON object keys could sometimes be named differently) but it works most of the time.</p>
<p>With the JSON mode (sometimes called structured output, or constrained decoding), we come back to our first approach, by prompting the LLM to generate JSON. But this time, we don’t have to nudge the LLM as much, because it must always generate valid JSON in output. It won’t add any Markdown markup, or any commentary.</p>
<p>Let’s see how to use the JSON mode with Gemini and LangChain4j:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-pro-002&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseMimeType</span>(<span style="color:#4070a0">&#34;application/json&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Notice how we set the response MIME type to application/json? That’s how we enable Gemini to always return valid JSON in output.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>List.<span style="color:#4070a0">of</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>SystemMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Return the name and age of the person described in the
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            biography below. Give the name and age in the form of
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            a JSON object following this structure:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            `{&#34;name&#34;: &#34;Jon Doe&#34;, &#34;age&#34;: 36}`
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;&#34;&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>UserMessage.<span style="color:#4070a0">from</span>(biography)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response.<span style="color:#4070a0">content</span>().<span style="color:#4070a0">text</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// {&#34;name&#34;: &#34;Anna&#34;, &#34;age&#34;: 23}</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>We just needed to encourage Gemini to follow the JSON structure shown in the example in the system instruction. We don’t have to give further nudges to the model to not output Markdown code markup, or to prevent it from adding extra explanations.</p>
<p>This gives great results, but to go even further and ensure that the returned JSON document is compliant with the format you really wish to get, you can also define a JSON response schema. That’s what we’re gonna see next.</p>
<h2 id="even-better-with-json-schema-for-structured-output">Even better with JSON schema for structured output</h2>
<p>In addition to the response MIME type, you can specify the JSON schema that the JSON response must comply with. Let’s complement the previous example, and add that schema definition:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-pro-002&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseMimeType</span>(<span style="color:#4070a0">&#34;application/json&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseSchema</span>(Schema.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setType</span>(Type.<span style="color:#4070a0">OBJECT</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">putProperties</span>(<span style="color:#4070a0">&#34;name&#34;</span>,<span style="color:#bbb"> </span>Schema.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">setType</span>(Type.<span style="color:#4070a0">STRING</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">setDescription</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#4070a0">&#34;The name of the person described in the biography&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">putProperties</span>(<span style="color:#4070a0">&#34;age&#34;</span>,<span style="color:#bbb"> </span>Schema.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">setType</span>(Type.<span style="color:#4070a0">INTEGER</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">setDescription</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#4070a0">&#34;The age of the person described in the biography&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">addAllRequired</span>(List.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;name&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;age&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>The response should be an object with a string name, and an integer age properties.</p>
<h2 id="bonus-points-with-type-safe-objects-with-langchain4j">Bonus points with type safe objects with LangChain4j</h2>
<p>In our LangChain4j based examples, in Java, each time, the low-level APIs offered by the framework always responded with JSON strings. But as a Java developer, we’d prefer to manipulate real Java objects instead. Of course, you can take advantage of the unmarshalling capabilities of your favorite JSON library. But what if the framework provided a higher level abstraction and did all the work for you? That’s where we’ll use LangChain4j’s AI services.</p>
<p>First, let’s define a data structure to hold the name and age of our biographies, with a Java record:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">Person</span>(String<span style="color:#bbb"> </span>name,<span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>age)<span style="color:#bbb"> </span>{<span style="color:#bbb"> </span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>The next step is to create a contract that the framework will implement for you. In input, a string biography, and in output, a Person record:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">interface</span> <span style="color:#0e84b5;font-weight:bold">PersonExtractor</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@SystemMessage</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Your role is to extract the name and age
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        of the person described in the biography.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Person<span style="color:#bbb"> </span><span style="color:#06287e">extractPerson</span>(String<span style="color:#bbb"> </span>biography);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Notice how we annotate the method with a system instruction that instructs the model what its role is.</p>
<p>We still need to instantiate our chat model:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-pro-002&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseMimeType</span>(<span style="color:#4070a0">&#34;application/json&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseSchema</span>(SchemaHelper.<span style="color:#4070a0">fromClass</span>(Person.<span style="color:#4070a0">class</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>We specify again the response MIME type, and also the response schema. But we’re using a convenience method provided by the SchemaHelper class to derive a schema from a Java class (here, our Person record).</p>
<p>Now we can instantiate our person extractor contract as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>PersonExtractor<span style="color:#bbb"> </span>extractor<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>AiServices.<span style="color:#4070a0">create</span>(PersonExtractor.<span style="color:#4070a0">class</span>,<span style="color:#bbb"> </span>model);<span style="color:#bbb">
</span></span></span></code></pre></div><p>And finally, we can pass it the biography in input:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Person<span style="color:#bbb"> </span>person<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>extractor.<span style="color:#4070a0">extractPerson</span>(bio);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(person.<span style="color:#4070a0">name</span>());<span style="color:#bbb">  </span><span style="color:#60a0b0;font-style:italic">// Anna</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(person.<span style="color:#4070a0">age</span>());<span style="color:#bbb">   </span><span style="color:#60a0b0;font-style:italic">// 23</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>We have an instance of our Person record in output that is properly populated with the name and age of the person described in our biography! That way, as Java developers, we manipulate a real Java object, in a type-safe manner! Our application is enhanced by an LLM, but from a developer perspective, we manipulate interfaces and objects.</p>
<h2 id="summary">Summary</h2>
<p>Lots of articles, videos, or presentations often talk about the chatbot use case, when creating applications powered by large language models. However, data extraction is another very important and useful task where LLMs shine.</p>
<p>In this article, we saw different approaches to do data extraction: via prompting, function calling, or with a JSON mode or JSON schema. If your LLM supports the ability to set a response schema, that’s definitely the best way to get the JSON output you expect.</p>
<p>Also, if the LLM orchestration framework you use supports it, be sure to check if it’s able to return type-safe objects that you can manipulate with your programming language directly, without having to parse the JSON string yourself.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Things you never dared to ask about LLMs</title><link>https://glaforge.dev/talks/2024/10/24/things-you-never-dared-to-ask-about-llms/</link><pubDate>Thu, 24 Oct 2024 16:53:04 +0200</pubDate><guid>https://glaforge.dev/talks/2024/10/24/things-you-never-dared-to-ask-about-llms/</guid><description>&lt;p>Along my learning journey about generative AI, lots of questions popped up in my mind.
I was very curious to learn how things worked under the hood in Large Language Models (at least having an intuition rather than knowing the maths in and out).
Sometimes, I would wonder about how tokens are created, or how hyperparameters influence text generation.&lt;/p>
&lt;p>Before the &lt;a href="https://www.dotai.io/">dotAI&lt;/a> conference, I was invited to talk at the meetup organised by &lt;a href="https://www.datastax.com/">DataStax&lt;/a>.
I presented about all those things you never dared to ask about LLMs, sharing both the questions I came up with while learning about generative AI, and the answers I found and discovered along the way.&lt;/p></description><content:encoded>
<![CDATA[<p>Along my learning journey about generative AI, lots of questions popped up in my mind.
I was very curious to learn how things worked under the hood in Large Language Models (at least having an intuition rather than knowing the maths in and out).
Sometimes, I would wonder about how tokens are created, or how hyperparameters influence text generation.</p>
<p>Before the <a href="https://www.dotai.io/">dotAI</a> conference, I was invited to talk at the meetup organised by <a href="https://www.datastax.com/">DataStax</a>.
I presented about all those things you never dared to ask about LLMs, sharing both the questions I came up with while learning about generative AI, and the answers I found and discovered along the way.</p>
<p>Without further ado, here&rsquo;s the deck:</p>
<script async class="speakerdeck-embed" data-id="476be803290048d6935e585bf87d1e5f" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<h2 id="abstract">Abstract</h2>
<blockquote>
<h2 id="things-you-never-dared-to-ask-about-llms">Things you never dared to ask about LLMs</h2>
<p>Large Language Models (LLMs) have taken the world by storm, powering applications from chatbots to content generation.
Yet, beneath the surface, these models remain enigmatic.</p>
<p>This presentation will “delve” into the hidden corners of LLM technology that often leave developers scratching their heads.
It’s time to ask those questions you’ve never dared ask about the mysteries underpinning LLMs.</p>
<p>Here are some questions we’ll to answer:</p>
<p>Do you wonder why LLMs spit tokens instead of words? Where do those tokens come from?</p>
<ul>
<li>What’s the difference between a “foundation” / “pre-trained” model, and an “instruction-tuned” one?</li>
<li>We’re often tweaking (hyper)parameters like temperature, top-p, top-k, but do you know how they really affect how tokens are picked up?</li>
<li>Quantization makes models smaller, but what are all those number encodings like fp32, bfloat16, int8, etc?</li>
<li>LLMs are good at translation, right? Do you speak the Base64 language too?</li>
</ul>
<p>We’ll realize together that LLMs are far from perfect:</p>
<ul>
<li>We’ve all heard about hallucinations, or should we say confabulations?</li>
<li>What is this reversal curse that makes LLMs ignore some facts from a different viewpoint?</li>
<li>You’d think that LLMs are deterministic at low temperature, but you’d be surprised by how the context influences LLMs’ answers…</li>
</ul>
<p>Buckle up, it’s time to dispel the magic of LLMs, and ask those questions we never dared to ask!</p></blockquote>
<p>This talk wasn&rsquo;t recorded, but I hope to give this presentation again sometime soon, and hopefully, it&rsquo;ll be recorded then.
If that happens, I&rsquo;ll share the video recording once it&rsquo;s available.</p>
<h2 id="illustrations-imagen-3-to-the-rescure">Illustrations: Imagen 3 to the rescure</h2>
<p>For those who are curious about the cute little robots that appear in this presentation,
I&rsquo;ve generated them with DeepMind&rsquo;s <a href="https://deepmind.google/technologies/imagen-3/">Imagen 3</a> image generation model.</p>
<p>The quality of the output was really lovely, and I might have been a bit overboard with the number of generated robots in this deck.</p>
<p>I would start pretty much all my prompts with <em>&ldquo;cartoon of a cute little robot&hellip;&rdquo;</em></p>
<p>For my Java developer friends, you can <a href="https://glaforge.dev/posts/2024/10/01/ai-nktober-generating-ink-drawings-with-imagen/">generate images with Imagen via LangChain4j</a>
(as explained in that article where I generated black&rsquo;n white ink drawings).</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Advanced RAG Techniques</title><link>https://glaforge.dev/talks/2024/10/14/advanced-rag-techniques/</link><pubDate>Mon, 14 Oct 2024 10:11:14 +0200</pubDate><guid>https://glaforge.dev/talks/2024/10/14/advanced-rag-techniques/</guid><description>&lt;p>&lt;strong>Retrieval Augmented Generation&lt;/strong> (RAG) is a pattern to let you prompt a large language model (LLM) about your own data, via in-context learning by providing extracts of documents found in a vector database (or potentially other sources too).&lt;/p>
&lt;p>Implementing RAG isn&amp;rsquo;t very complicated, but the results you get are not necessarily up to your expectations. In the presentations below, I explore various &lt;strong>advanced techniques to improve the quality of the responses returned by your RAG system&lt;/strong>:&lt;/p></description><content:encoded>
<![CDATA[<p><strong>Retrieval Augmented Generation</strong> (RAG) is a pattern to let you prompt a large language model (LLM) about your own data, via in-context learning by providing extracts of documents found in a vector database (or potentially other sources too).</p>
<p>Implementing RAG isn&rsquo;t very complicated, but the results you get are not necessarily up to your expectations. In the presentations below, I explore various <strong>advanced techniques to improve the quality of the responses returned by your RAG system</strong>:</p>
<p>Ingestion chunking techniques like:</p>
<ul>
<li>Embedding of sliding windows of sentences</li>
<li>Hypothetical question embedding</li>
<li>Contextual retrieval embedding (invented recently by Anthropic)</li>
<li>Semantic chunking (created by Greg Kamradt)</li>
</ul>
<p>Retrieval techniques, including:</p>
<ul>
<li>Query compression</li>
<li>Hypothetical Document Embedding (HyDE)</li>
</ul>
<p>And I also mention how an <em>agentic</em> approach can help for more advanced and complex needs, with providing intermerdiary results, combined in a final response. <strong>Agentic RAG</strong> is a very important and promising approach that I&rsquo;ll certainly come back to in upcoming articles.</p>
<p>At Devoxx Belgium 2024, I gave a 50-minute session, and a 3-hour long deep dive with my friend <a href="https://x.com/clunven">Cédrick Lunven</a> from Datastax (we used the great <a href="https://www.datastax.com/products/datastax-astra">Astra DB</a> vector database in our demos). You&rsquo;ll find both decks and videos below.</p>
<h2 id="code-available-on-github">Code available on Github</h2>
<p>All the code presented in those sessions is available in this <a href="https://github.com/datastaxdevs/conference-2024-devoxx/">Github repository</a></p>
<h2 id="rag-from-dumb-implementation-to-serious-results">RAG: from dumb implementation to serious results</h2>
<h3 id="abstract">Abstract</h3>
<blockquote>
<p>Embarking on your RAG journey may seem effortless, but achieving satisfying results often proves challenging. Inaccurate, incomplete, or outdated answers, suboptimal document retrieval, and poor text chunking can quickly dampen your initial enthusiasm.</p>
<p>In this session, we&rsquo;ll leverage LangChain4j to elevate your RAG implementations. We&rsquo;ll explore:</p>
<ul>
<li>Advanced Chunking Strategies: Optimize document segmentation for improved context and relevance.</li>
<li>Query Refinement Techniques: Expand and compress queries to enhance retrieval accuracy.</li>
<li>Metadata Filtering: Leverage metadata to pinpoint the most relevant documents.</li>
<li>Document Reranking: Reorder retrieved documents for optimal result presentation.</li>
<li>Data Lifecycle Management: Implement processes to maintain data freshness and relevance.</li>
<li>Evaluation and Presentation: Assess the effectiveness of your RAG pipeline and deliver results that meet user expectations.</li>
</ul>
<p>Join us as we transform your simplistic RAG experience from one of frustration to delight your users with meaningful and accurate answers.</p></blockquote>
<h3 id="presentation-slide-deck">Presentation slide deck</h3>
<script async class="speakerdeck-embed" data-id="a2207c4bc9b9447da5a397107da19d0f" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<h3 id="youtube-video-recording">YouTube video recording</h3>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/6_wUUYKBdE0?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<h2 id="rag-from-dumb-implementation-to-serious-results-1">RAG: from dumb implementation to serious results</h2>
<h3 id="abstract-1">Abstract</h3>
<blockquote>
<p>It’s easy to get started with Retrieval Augmented Generation, but you’ll quickly be disappointed with the generated answers: inaccurate or incomplete, missing context or outdated information, bad text chunking strategy, not the best documents returned by your vector database, and the list goes on.</p>
<p>After meeting thousands of developers across Europe, we’ve explored those pain points, and will share with you how to overcome them. As part of the team building a vector database we are aware of the different flavors of searches (semantic, meta-data, full text, multimodal) and embedding model choices. We have been implementing RAG pipelines across different projects and frameworks and are contributing to LangChain4j.</p>
<p>In this deep-dive, we will examine various techniques using LangChain4j to bring your RAG to the next level: with semantic chunking, query expansion &amp; compression, metadata filtering, document reranking, data lifecycle processes, and how to best evaluate and present the results to your users.</p></blockquote>
<h3 id="presentation-slide-deck-1">Presentation slide deck</h3>
<script async class="speakerdeck-embed" data-id="5f7120a2dbeb4ffd917102321231cbc0" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<h3 id="youtube-video-recording-1">YouTube video recording</h3>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/RN7thifOmkI?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A Gemini and Gemma tokenizer in Java</title><link>https://glaforge.dev/posts/2024/10/04/a-gemini-and-gemma-tokenizer-in-java/</link><pubDate>Fri, 04 Oct 2024 15:41:10 +0200</pubDate><guid>https://glaforge.dev/posts/2024/10/04/a-gemini-and-gemma-tokenizer-in-java/</guid><description>&lt;p>It&amp;rsquo;s always interesting to know &lt;em>how the sausage is made&lt;/em>, don&amp;rsquo;t you think?
That&amp;rsquo;s why, a while ago, I looked at
&lt;a href="https://glaforge.dev/posts/2024/02/05/visualize-palm-based-llm-tokens/">embedding model tokenization&lt;/a>,
and I implemented a little &lt;a href="https://tokens-lpj6s2duga-ew.a.run.app/">visualization&lt;/a> to see the tokens in a colorful manner.
Yet, I was still curious to see how Gemini would tokenize text&amp;hellip;&lt;/p>
&lt;p>Both LangChain4j Gemini modules (from
&lt;a href="https://docs.langchain4j.dev/integrations/language-models/google-vertex-ai-gemini">Vertex AI&lt;/a> and from
&lt;a href="https://docs.langchain4j.dev/integrations/language-models/google-ai-gemini">Google AI Labs&lt;/a>)
can count the tokens included in a piece of text.
However, both do so by calling a REST API endpoint method called &lt;code>countTokens&lt;/code>.
This is not ideal, as it requires a network hop to get the token counts, thus adding undesired extra latency.
Wouldn&amp;rsquo;t it be nicer if we could count tokens locally instead?&lt;/p></description><content:encoded>
<![CDATA[<p>It&rsquo;s always interesting to know <em>how the sausage is made</em>, don&rsquo;t you think?
That&rsquo;s why, a while ago, I looked at
<a href="https://glaforge.dev/posts/2024/02/05/visualize-palm-based-llm-tokens/">embedding model tokenization</a>,
and I implemented a little <a href="https://tokens-lpj6s2duga-ew.a.run.app/">visualization</a> to see the tokens in a colorful manner.
Yet, I was still curious to see how Gemini would tokenize text&hellip;</p>
<p>Both LangChain4j Gemini modules (from
<a href="https://docs.langchain4j.dev/integrations/language-models/google-vertex-ai-gemini">Vertex AI</a> and from
<a href="https://docs.langchain4j.dev/integrations/language-models/google-ai-gemini">Google AI Labs</a>)
can count the tokens included in a piece of text.
However, both do so by calling a REST API endpoint method called <code>countTokens</code>.
This is not ideal, as it requires a network hop to get the token counts, thus adding undesired extra latency.
Wouldn&rsquo;t it be nicer if we could count tokens locally instead?</p>
<p>Interestingly, both Gemini and the open-weights <a href="https://ai.google.dev/gemma">Gemma</a>
models share the same tokenizer and token vocabulary.
Also, the tokenizer is based on <a href="https://github.com/google/sentencepiece">SentencePiece</a>,
which is a tokenizer/detokenizer implementing the byte-pair-encoding (BPE) and unigram language algorithms.</p>
<p>If you look at the <a href="https://huggingface.co/google/gemma-2-9b-it/tree/main">Gemma code on HuggingFace</a>,
you&rsquo;ll see a <code>tokenizer.json</code> file that you can open to see the available tokens in the vocabulary,
and a <code>tokenizer.model</code> file which is some kind of binary compressed variation.</p>
<p>Knowing that the list of tokens supported by Gemini and Gemma is available in those files, and how they are encoded,
I was curious to see if I could implement a Java tokenizer that could run locally, rather than calling a remote endpoint.</p>
<p>The <code>SentencePiece</code> implementation from Google is a C++ library, but I didn&rsquo;t really feel like wrapping it myself with JNI,
and fortunately, I discovered that the <a href="https://djl.ai/">DJL</a> project had done the JNI wrapping job already.</p>
<p>So let&rsquo;s see how to tokenize text for Gemini and Gemma, in Java!</p>
<h2 id="gemini-and-gemma-tokenization-in-java-with-djl">Gemini and Gemma tokenization in Java with DJL</h2>
<p>First of all, let&rsquo;s setup the dependency on DJL&rsquo;s <code>SentencePiece</code> module:</p>
<ul>
<li>From Maven:</li>
</ul>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>ai.djl.sentencepiece<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>sentencepiece<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>0.30.0<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div><ul>
<li>From Gradle:</li>
</ul>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>implementation <span style="color:#4070a0">&#39;ai.djl.sentencepiece:sentencepiece:0.30.0&#39;</span>
</span></span></code></pre></div><p>I saved the <code>tokenizer.model</code> file locally.
Note that it&rsquo;s a 4MB file, as Gemini/Gemma have a very large vocabulary of around a quarter million of tokens!</p>
<p>Now, let&rsquo;s instantiate an <code>SpTokenizer</code> object that loads this vocabulary file, and tokenize some text:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">ai.djl.sentencepiece.SpTokenizer</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Path<span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Paths.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;src/test/resources/gemini/tokenizer.model&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#902000">byte</span><span style="color:#666">[]</span><span style="color:#bbb"> </span>modelFileBytes<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Files.<span style="color:#4070a0">readAllBytes</span>(model);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(SpTokenizer<span style="color:#bbb"> </span>tokenizer<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>SpTokenizer(modelFileBytes))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>List<span style="color:#666">&lt;</span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>tokens<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>tokenizer.<span style="color:#4070a0">tokenize</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    When integrating an LLM into your application to extend it and \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    make it smarter, it&#39;s important to be aware of the pitfalls and \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    best practices you need to follow to avoid some common problems \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    and integrate them successfully. This article will guide you \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    through some key best practices that I&#39;ve come across.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(String<span style="color:#bbb"> </span>token:<span style="color:#bbb"> </span>tokens)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">format</span>(<span style="color:#4070a0">&#34;[%s]%n&#34;</span>,<span style="color:#bbb"> </span>token);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;Token count: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>tokens.<span style="color:#4070a0">size</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>When running this Java class, you&rsquo;ll see the following output:</p>
<pre tabindex="0"><code>[When]
[▁integrating]
[▁an]
[▁L]
[LM]
[▁into]
[▁your]
[▁application]
...

Token count: 61
</code></pre><h2 id="next-steps">Next steps</h2>
<p>Do we need next steps? Yes, why not!
My idea is to contribute a tokenizer module to LangChain4j,
so that the Vertex AI Gemini and the Google AI Gemini modules can both import it,
instead of relying on remote endpoint calls to count tokens.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>AI Inktober — Generating ink drawings with Imagen 3</title><link>https://glaforge.dev/posts/2024/10/01/ai-nktober-generating-ink-drawings-with-imagen/</link><pubDate>Mon, 30 Sep 2024 21:25:46 +0200</pubDate><guid>https://glaforge.dev/posts/2024/10/01/ai-nktober-generating-ink-drawings-with-imagen/</guid><description>&lt;p>Every year, in October, takes place the &lt;a href="https://inktober.com/">Inktober challenge&lt;/a>:
every day of the month, you have to do a drawing representing the word of the day.
The list of &lt;em>prompts&lt;/em> this year is the following:&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-8fe9d17b97ed78189b0ebc708c476b25">
&lt;img src="https://glaforge.dev/img/ainktober/prompts.png"
alt="Inktober 2024 prompts"
/>
&lt;/a>
&lt;figcaption>Inktober 2024 prompts&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-8fe9d17b97ed78189b0ebc708c476b25">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/ainktober/prompts.png"
alt="Inktober 2024 prompts"
/>
&lt;div class="lightbox-caption">Inktober 2024 prompts&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>I participated to some of the daily challenges the past few years, but I never did all of them.
But this year, for the fun, I thought I could ask Google&amp;rsquo;s
&lt;a href="https://deepmind.google/technologies/imagen-3/">Imagen 3&lt;/a> image model to draw for me!
(Or at least to draw something I could try to reproduce.)&lt;/p></description><content:encoded>
<![CDATA[<p>Every year, in October, takes place the <a href="https://inktober.com/">Inktober challenge</a>:
every day of the month, you have to do a drawing representing the word of the day.
The list of <em>prompts</em> this year is the following:</p>
<p><figure>
  <a href="#img-8fe9d17b97ed78189b0ebc708c476b25">
    <img src="/img/ainktober/prompts.png"
      alt="Inktober 2024 prompts"
       />
  </a>
  <figcaption>Inktober 2024 prompts</figcaption>
</figure>
<div class="lightbox" id="img-8fe9d17b97ed78189b0ebc708c476b25">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/ainktober/prompts.png"
    alt="Inktober 2024 prompts"
     />
  <div class="lightbox-caption">Inktober 2024 prompts</div>
</div>
</p>
<p>I participated to some of the daily challenges the past few years, but I never did all of them.
But this year, for the fun, I thought I could ask Google&rsquo;s
<a href="https://deepmind.google/technologies/imagen-3/">Imagen 3</a> image model to draw for me!
(Or at least to draw something I could try to reproduce.)</p>
<p>Of course, the goal of the challenge is not to generate images with the help of an AI.
On the contrary, the idea is about the pleasure you can have drawing yourself, with your own hands!
However, I was curious to see how Imagen would perform on such a challenge.</p>
<p>So I fired up my favorite Java AI framework: <a href="https://docs.langchain4j.dev/">LangChain4j</a>,
as it supports Imagen 3, as image model.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>Imagen 3 is generally available on Google Cloud&rsquo;s Vertex AI platform, but it&rsquo;s behind an <em>allow list</em>.
So you have to <a href="https://docs.google.com/forms/d/1cqt9padvfMgqn23W5FMPTqh7bW1KLkEOsC5G6uC-uuM/viewform">request access</a> to be able to use it.</p>
      </div>
    </div><p>You will need the following dependency:</p>
<ul>
<li>For Gradle users:</li>
</ul>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>implementation <span style="color:#4070a0">&#39;dev.langchain4j:langchain4j-vertex-ai:0.35.0&#39;</span>
</span></span></code></pre></div><ul>
<li>For Maven users:</li>
</ul>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>dev.langchain4j<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>langchain4j-vertex-ai<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>0.35.0<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div><p>Now let&rsquo;s have a look at the code:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.data.image.Image</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.model.output.Response</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.model.vertexai.VertexAiImageModel</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.nio.file.Path</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">AInktober</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">main</span>(String<span style="color:#666">[]</span><span style="color:#bbb"> </span>args)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>VertexAiImageModel<span style="color:#bbb"> </span>imagenModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiImageModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">endpoint</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_VERTEXAI_ENDPOINT&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">publisher</span>(<span style="color:#4070a0">&#34;google&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;imagen-3.0-fast-generate-001&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">aspectRatio</span>(VertexAiImageModel.<span style="color:#4070a0">AspectRatio</span>.<span style="color:#4070a0">SQUARE</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">negativePrompt</span>(<span style="color:#4070a0">&#34;watercolor, gray shades&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">persistTo</span>(Path.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;/tmp/imagen&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>prompt<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            A black and white ink drawing of a
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            backpack, on a fully white background
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;&#34;&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Response<span style="color:#666">&lt;</span>Image<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>imageResponse<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>imagenModel.<span style="color:#4070a0">generate</span>(prompt);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(imageResponse.<span style="color:#4070a0">content</span>().<span style="color:#4070a0">url</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><ul>
<li>I have set up several environment variables containing my Google Cloud project details.</li>
<li>I decided to use <code>imagen-3.0-fast-generate-001</code>, which generates images faster (and cheaper!) than <code>imagen-3.0-generate-001</code>
at the cost of a slightly lower quality (but for ink drawings, that&rsquo;s not really visible).</li>
<li>I went with square images, but you can use landscape, portrait, and wider variants too.</li>
<li>I added a negative prompt, because some images looked a bit more like watercolor at times, but I wanted images more black and white.</li>
<li>I persist all the generated images into a temporary folder.</li>
<li>My prompt contains the first subject of the day, a <em>&ldquo;backpack&rdquo;</em>, and I specify that I want a black and white ink drawing,
but I also added that I wanted a white background, as sometimes the background can be fully black, or some sepia shade.</li>
</ul>
<p>So what does the first image look like?</p>
<p><figure>
  <a href="#img-a3f0dadbd4b24ec339369e1cd9e0d2d3">
    <img src="/img/ainktober/ainktober-01-backpack.png"
      alt="Inktober 2024&rsquo;s backpack"
       />
  </a>
  <figcaption>Inktober 2024&rsquo;s backpack</figcaption>
</figure>
<div class="lightbox" id="img-a3f0dadbd4b24ec339369e1cd9e0d2d3">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/ainktober/ainktober-01-backpack.png"
    alt="Inktober 2024&rsquo;s backpack"
     />
  <div class="lightbox-caption">Inktober 2024&rsquo;s backpack</div>
</div>
</p>
<p>It definitely looks like an ink drawing of a backpack!</p>
<p>Don&rsquo;t worry, I won&rsquo;t post a new article each day for the new daily image prompt.
Instead, I&rsquo;ll share the other days on my usual social media channels (see the bottom of the blog to find them out.)</p>
<p>Be sure to checkout Imagen 3, it&rsquo;s pretty good!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Lots of new cool Gemini stuff in LangChain4j 0.35.0</title><link>https://glaforge.dev/posts/2024/09/29/lots-of-new-cool-gemini-stuff-in-langchain4j/</link><pubDate>Wed, 25 Sep 2024 13:41:19 +0200</pubDate><guid>https://glaforge.dev/posts/2024/09/29/lots-of-new-cool-gemini-stuff-in-langchain4j/</guid><description>&lt;p>While &lt;a href="https://docs.langchain4j.dev/">LangChain4j&lt;/a> 0.34 introduced my
&lt;a href="http://localhost:1313/posts/2024/09/05/new-gemini-model-in-langchain4j/">new Google AI Gemini module&lt;/a>,
a new 0.35.0 version is already here today, with some more cool stuff for Gemini and Google Cloud!&lt;/p>
&lt;p>Let&amp;rsquo;s have a look at what&amp;rsquo;s in store!&lt;/p>
&lt;h2 id="gemini-15-pro-002-and-gemini-15-flash-002">Gemini 1.5 Pro 002 and Gemini 1.5 Flash 002&lt;/h2>
&lt;p>This week, &lt;a href="https://developers.googleblog.com/en/updated-production-ready-gemini-models-reduced-15-pro-pricing-increased-rate-limits-and-more/">Google announced&lt;/a>
the release of the new versions of the Google 1.5 models:&lt;/p>
&lt;ul>
&lt;li>&lt;code>google-1.5-pro-002&lt;/code>&lt;/li>
&lt;li>&lt;code>google-1.5-flash-002&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>Of course, both models are supported by LangChain4j!
The Google AI Gemini module also supports the &lt;code>gemini-1.5-flash-8b-exp-0924&lt;/code> 8-billion parameter model.&lt;/p></description><content:encoded>
<![CDATA[<p>While <a href="https://docs.langchain4j.dev/">LangChain4j</a> 0.34 introduced my
<a href="http://localhost:1313/posts/2024/09/05/new-gemini-model-in-langchain4j/">new Google AI Gemini module</a>,
a new 0.35.0 version is already here today, with some more cool stuff for Gemini and Google Cloud!</p>
<p>Let&rsquo;s have a look at what&rsquo;s in store!</p>
<h2 id="gemini-15-pro-002-and-gemini-15-flash-002">Gemini 1.5 Pro 002 and Gemini 1.5 Flash 002</h2>
<p>This week, <a href="https://developers.googleblog.com/en/updated-production-ready-gemini-models-reduced-15-pro-pricing-increased-rate-limits-and-more/">Google announced</a>
the release of the new versions of the Google 1.5 models:</p>
<ul>
<li><code>google-1.5-pro-002</code></li>
<li><code>google-1.5-flash-002</code></li>
</ul>
<p>Of course, both models are supported by LangChain4j!
The Google AI Gemini module also supports the <code>gemini-1.5-flash-8b-exp-0924</code> 8-billion parameter model.</p>
<p>Versions <code>002</code> come with:</p>
<ul>
<li>much improved math and reasoning capabilities <br />
(7%-20% increase depending on the benchmark),</li>
<li>2x faster output, and 3x lower latency,</li>
<li>and also roughly a 50% price cut!</li>
</ul>
<h2 id="google-cloud-storage-document-loader">Google Cloud Storage document loader</h2>
<p>When implementing Retrieval Augmented Generation (RAG), you must load the documents from somewhere.
You can feed the docs directly in the context, but LangChain4j comes with the notion of <a href="https://docs.langchain4j.dev/tutorials/rag#document-loader">document loaders</a>.
There are existing document loaders for the file system, for files at remote URLs, or source files stored in Github.</p>
<p>In this release, I&rsquo;ve implemented a <strong>Google Cloud Storage document loader</strong>,
which lets you reference documents stored inside cloud storage buckets.</p>
<p>Create a GCS document loader with the new builder:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>gcsLoader<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleCloudStorageDocumentLoader.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Then you can load a single document, and parse it:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Document<span style="color:#bbb"> </span>document<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>gcsLoader.<span style="color:#4070a0">loadDocument</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;BUCKET_NAME&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;FILE_NAME.txt&#34;</span>,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>TextDocumentParser());<span style="color:#bbb">
</span></span></span></code></pre></div><p>All the documents in a bucket:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>List<span style="color:#666">&lt;</span>Document<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>documents<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>gcsLoader.<span style="color:#4070a0">loadDocuments</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;BUCKET_NAME&#34;</span>,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>TextDocumentParser());<span style="color:#bbb">
</span></span></span></code></pre></div><p>Or just the a list of files filtered with a <em>glob</em> pattern:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>List<span style="color:#666">&lt;</span>Document<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>documents<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>gcsLoader.<span style="color:#4070a0">loadDocuments</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;BUCKET_NAME&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;*.txt&#34;</span>,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>TextDocumentParser());<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="vertex-ai-ranking-api">Vertex AI Ranking API</h2>
<p>When implementing Retrieval Augmented Generation (RAG), your vector database returns a certain number of results.
They are usually sorted by vector similarity.
But it&rsquo;s not necessarily because the vectors have the highest similarity, that they are necessarily the best matches to answer a user query.
In order to palliate this problem, there are ranking or reranking APIs and models that exist to order results according to how well they match the query.</p>
<p>The Vertex AI platform from Google Cloud offers a <a href="https://cloud.google.com/generative-ai-app-builder/docs/ranking">ranking API</a>
for that purpose, a little known API that deserves more awareness.
I implemented a <code>ScoringModel</code> for this Vertex AI Ranking API:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>VertexAiScoringModel<span style="color:#bbb"> </span>scoringModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiScoringModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">projectId</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">projectNumber</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_NUMBER&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">projectLocation</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;semantic-ranker-512&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>List<span style="color:#666">&lt;</span>Double<span style="color:#666">&gt;&gt;</span><span style="color:#bbb"> </span>score<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>scoringModel.<span style="color:#4070a0">scoreAll</span>(Stream.<span style="color:#4070a0">of</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;The sky appears blue due to a phenomenon called Rayleigh &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;scattering. Sunlight is comprised of all the colors of &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;the rainbow. Blue light has shorter wavelengths than other &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;colors, and is thus scattered more easily.&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;A canvas stretched across the day,\n&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Where sunlight learns to dance and play.\n&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Blue, a hue of scattered light,\n&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;A gentle whisper, soft and bright.&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>).<span style="color:#4070a0">map</span>(TextSegment::from).<span style="color:#4070a0">collect</span>(Collectors.<span style="color:#4070a0">toList</span>()),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;Why is the sky blue?&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// [0.8199999928474426, 0.4300000071525574]</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>In the example above, a user asks <em>why the sky is blue</em>.
The Ranking API attempts to determine which of two excerpts best matches this question.
The first excerpt appears to be an explanation of this celestial phenomenon, while the second sounds more like a poem.
When scoring these text fragments, we observe that the first one has a higher value (0.82 vs. 0.43).</p>
<p>It is also possible to score just one piece of text with the <code>score(text, query)</code> and <code>score(segment, query)</code> methods.</p>
<p>Now what&rsquo;s interesting is that this LangChain4j notion of scoring models is also well integrated in the RAG pipeline:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>VertexAiScoringModel<span style="color:#bbb"> </span>scoringModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiScoringModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">projectId</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">projectNumber</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_NUM&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">projectLocation</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">model</span>(<span style="color:#4070a0">&#34;semantic-ranker-512&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ContentAggregator<span style="color:#bbb"> </span>contentAggregator<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>ReRankingContentAggregator.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">scoringModel</span>(scoringModel)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>...<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>RetrievalAugmentor<span style="color:#bbb"> </span>retrievalAugmentor<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>DefaultRetrievalAugmentor.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>...<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">contentAggregator</span>(contentAggregator)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>AiServices.<span style="color:#4070a0">builder</span>(Assistant.<span style="color:#4070a0">class</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">chatLanguageModel</span>(...)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">retrievalAugmentor</span>(retrievalAugmentor)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>When creating the AI service, you specify the chat model to use.
Additionally, you can integrate a <em>retrieval augmentor</em>, which allows you to configure a <em>content aggregator</em>.
The content aggregator, in turn, can specify a <em>scoring model</em>.
This process involves three steps, but it enables you to leverage the ranking of semantic search results when implementing RAG.
This means you can prioritize the most relevant results based on their semantic similarity, not solely on their vector similarity.</p>
<h2 id="new-parameters-for-the-vertex-ai-embedding-models">New parameters for the Vertex AI embedding models</h2>
<p>Embedding models are critical for RAG, and LangChain4j has had support for the Google Cloud Vertex AI embedding models for a long time.
But there are a couple of new flags that have recently been introduced:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>EmbeddingModel<span style="color:#bbb"> </span>embeddingModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiEmbeddingModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(PROJECT_ID)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(<span style="color:#4070a0">&#34;us-central1&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">publisher</span>(<span style="color:#4070a0">&#34;google&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(MODEL_NAME)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">autoTruncate</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">outputDimensionality</span>(512)<span style="color:#bbb">
</span></span></span></code></pre></div><p>The <code>autoTruncate(true)</code> method automatically truncates text to embed to a maximum of 2048 tokens.
If your input is longer than this limit, you would get an error from the model.
With auto-truncation, no more error, but if your text is truncated, you might miss a bit of meaning from the part that was cut off.</p>
<p>The other new method is <code>outputDimensionality(512)</code>.
The Vertex AI embedding models usually default to 768-dimensional vectors.
However, our <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-text-embeddings">latest embedding models</a>
are <a href="https://huggingface.co/blog/matryoshka"><em>Matryoshka</em> embedding models</a>,
which means that the most meaningful values in the vector comes first.
So when you do vector comparisons, you can make calculations quicker if you focus on the lowest dimensions,
and with this new method, you can just return vectors with less dimensions directly.</p>
<h2 id="google-ai-embedding-model">Google AI embedding model</h2>
<p>Speaking of embedding models, if you use the Google AI Gemini model instead of the Vertex AI flavor,
you can now also access our embedding models without relying on the Vertex AI models,
thanks to the new embedding model for Google AI:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>embeddingModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiEmbeddingModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(GOOGLE_AI_GEMINI_API_KEY)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;embedding-001&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">maxRetries</span>(3)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">logRequestsAndResponses</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">titleMetadataKey</span>(<span style="color:#4070a0">&#34;title&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">taskType</span>(GoogleAiEmbeddingModel.<span style="color:#4070a0">TaskType</span>.<span style="color:#4070a0">RETRIEVAL_DOCUMENT</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">outputDimensionality</span>(512)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>This new embedding model is the same as the one coming from Vertex AI, and has the same feature set.</p>
<h2 id="google-ai-gemini-token-count-estimation-and-tokenizer">Google AI Gemini token count estimation and tokenizer</h2>
<p>The Google AI Gemini model implements the <code>TokenCountEstimator</code> interface,
which means you can use the <code>estimateTokenCount()</code> method to count tokens:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>gemini<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(GOOGLE_AI_GEMINI_API_KEY)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#902000">int</span><span style="color:#bbb"> </span>countedTokens<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>gemini.<span style="color:#4070a0">estimateTokenCount</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;What is the capital of France?&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>There is also now a <code>GoogleAiGeminiTokenizer</code> class, implementing the misnamed <code>Tokenizer</code> interface
(misnamed because it&rsquo;s not tokenizing text, it&rsquo;s just counting tokens):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>geminiTokenizer<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiTokenizer.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(GOOGLE_AI_GEMINI_API_KEY)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#902000">int</span><span style="color:#bbb"> </span>count<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>tokenizer.<span style="color:#4070a0">estimateTokenCountInText</span>(<span style="color:#4070a0">&#34;Hello world!&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Note that both the <code>estimateTokenCount()</code> method and the <code>GoogleAiGeminiTokenizer</code> call a remote API endpoint.
They don&rsquo;t use a tokenizer class to count the tokens, so those calls incur some network hops.</p>
<p>What&rsquo;s interesting with the <code>Tokenizer</code>s is that they can be used by document splitters to split documents according to the number of tokens, rather than by characters or other boundaries:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>DocumentSplitter<span style="color:#bbb"> </span>splitter<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>DocumentSplitters.<span style="color:#4070a0">recursive</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>maxSegmentSizeInTokens,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>maxOverlapSizeInTokens,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>geminiTokenizer);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Currently, only the Google AI module implements this <code>Tokenizer</code> interface, but it can be used with the Vertex AI Gemini module as well.
But later down the road, I think I&rsquo;ll also implement it for the Vertex AI module.</p>
<h2 id="chat-listener-support">Chat listener support</h2>
<p>Both the Google AI Gemini and the Vertex AI modules implement the new chat listener support.</p>
<ul>
<li>Vertex AI Gemini:</li>
</ul>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-pro-002&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">listeners</span>(singletonList(listener))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><ul>
<li>Google AI Gemini:</li>
</ul>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash-002&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">listeners</span>(singletonList(listener))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Let&rsquo;s have a look at the listener interface, which allows you to listen to model requests, responses, and errors:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">interface</span> <span style="color:#0e84b5;font-weight:bold">ChatModelListener</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">default</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">onRequest</span>(ChatModelRequestContext<span style="color:#bbb"> </span>reqContext)<span style="color:#bbb"> </span>{...}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">default</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">onResponse</span>(ChatModelResponseContext<span style="color:#bbb"> </span>respContext)<span style="color:#bbb"> </span>{...}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">default</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">onError</span>(ChatModelErrorContext<span style="color:#bbb"> </span>errContext)<span style="color:#bbb"> </span>{...}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>The various <code>*Context</code> parameters contain various details about how the model was parameterized, what the prompt was, or what error was encountered.</p>
<p>It might be interesting to follow the recent <a href="https://opentelemetry.io/docs/specs/semconv/gen-ai/">OpenTelemetry GenAI recommendations</a>
and implement a listener that directly plugs into your observability solution!</p>
<h2 id="enum-structured-output">Enum structured output</h2>
<p>I&rsquo;ll finish the laundry list of features with the <strong>enum</strong> structured output.</p>
<p>The Gemini models have great support for structured output.
Not only can you ask for JSON outputs, but you can also specify a JSON schema so that the model follows that schema for generating its JSON response.
This is of utmost importance for deterministic parseable results that fit well with your strongly typed programming language.</p>
<p>Gemini lets you return arbitray JSON objects and arrays.
But for tasks like classification or sentiment analysis, it is also able to return a single enum value, rather than a JSON object that would have a property containing the value.</p>
<ul>
<li>Vertex AI Gemini:</li>
</ul>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#bbb"> </span>VertexAiGeminiChatModel<span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GCP_LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(GEMINI_1_5_PRO)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseSchema</span>(Schema.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setType</span>(Type.<span style="color:#4070a0">STRING</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">addAllEnum</span>(Arrays.<span style="color:#4070a0">asList</span>(<span style="color:#4070a0">&#34;POSITIVE&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;NEUTRAL&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;NEGATIVE&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(asList(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>SystemMessage.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Your role is to analyse the sentiment of user&#39;s messages&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;This is super exciting news, congratulations!&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response.<span style="color:#4070a0">content</span>().<span style="color:#4070a0">text</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// POSITIVE</span><span style="color:#bbb">
</span></span></span></code></pre></div><ul>
<li>Google AI Gemini:</li>
</ul>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>GoogleAiGeminiChatModel<span style="color:#bbb"> </span>gemini<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseFormat</span>(ResponseFormat.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">type</span>(JSON)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">jsonSchema</span>(JsonSchema.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">rootElement</span>(JsonObjectSchema.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">properties</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>LinkedHashMap<span style="color:#666">&lt;</span>String,<span style="color:#bbb"> </span>JsonSchemaElement<span style="color:#666">&gt;</span>()<span style="color:#bbb"> </span>{{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>put(<span style="color:#4070a0">&#34;sentiment&#34;</span>,<span style="color:#bbb"> </span>JsonEnumSchema.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span>.<span style="color:#4070a0">enumValues</span>(<span style="color:#4070a0">&#34;POSITIVE&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;NEUTRAL&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;NEGATIVE&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span>.<span style="color:#4070a0">build</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>}})<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ChatResponse<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>gemini.<span style="color:#4070a0">chat</span>(ChatRequest.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">messages</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>SystemMessage.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;Your role is to analyse the sentiment of user&#39;s messages&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;This is super exciting news, congratulations!&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response.<span style="color:#4070a0">aiMessage</span>().<span style="color:#4070a0">text</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// POSITIVE</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>This is particularly useful for all sorts of classification tasks!</p>
<h2 id="documentation-updates">Documentation updates</h2>
<p>As I often used to say when working on the Apache Groovy project:</p>
<blockquote>
<p>&ldquo;A feature doesn&rsquo;t exist if it&rsquo;s not documented.&rdquo;</p>
<p>— Guillaume Laforge</p></blockquote>
<p>With that motto in mind, I thought it was high time that I expanded the documentation for the Gemini related pages of the LangChain4j documentation:</p>
<ul>
<li><a href="https://docs.langchain4j.dev/integrations/document-loaders/google-cloud-storage">Google Cloud Storage document loader</a></li>
<li><a href="https://docs.langchain4j.dev/integrations/scoring-reranking-models/vertex-ai">Google Cloud Ranking API</a></li>
<li><a href="https://docs.langchain4j.dev/integrations/embedding-models/google-vertex-ai">Vertex AI embedding models</a></li>
<li><a href="https://docs.langchain4j.dev/integrations/language-models/google-ai-gemini">Google AI Gemini models</a></li>
<li><a href="https://docs.langchain4j.dev/integrations/language-models/google-vertex-ai-gemini">Google Cloud Vertex AI Gemini models</a></li>
</ul>
<h2 id="tell-me-what-you-use-langchain4js-gemini-support-for">Tell me what you use LangChain4j&rsquo;s Gemini support for!</h2>
<p>I&rsquo;m really curious about what you are developing with LangChain4j, and even more so, if you&rsquo;re using the various Gemini components.
Don&rsquo;t hesitate to reach out to me, via the social media platforms mentioned below!
I&rsquo;m also interested in the features you&rsquo;d like to see prioritized and implemented.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Some advice and good practices when integrating an LLM in your application</title><link>https://glaforge.dev/posts/2024/09/23/some-good-practices-when-integrating-an-llm-in-your-application/</link><pubDate>Mon, 23 Sep 2024 17:45:17 +0200</pubDate><guid>https://glaforge.dev/posts/2024/09/23/some-good-practices-when-integrating-an-llm-in-your-application/</guid><description>&lt;p>When integrating an LLM into your applicaton to extend it and make it smarter, it&amp;rsquo;s important to be aware of the pitfalls and best practices you need to follow to avoid some common problems and integrate them successfully. This article will guide you through some key best practices that I&amp;rsquo;ve come across.&lt;/p>
&lt;h2 id="understanding-the-challenges-of-implementing-llms-in-real-world-applications">Understanding the Challenges of Implementing LLMs in Real-World Applications&lt;/h2>
&lt;p>One of the first challenges is that LLMs are constantly being improved. This means that the model you start using could change under the hood, and suddenly your application doesn&amp;rsquo;t work as it did before. Your prompts might need adjustments to work with the newer version, or worse, they might even lead to unintended results!&lt;/p></description><content:encoded>
<![CDATA[<p>When integrating an LLM into your applicaton to extend it and make it smarter, it&rsquo;s important to be aware of the pitfalls and best practices you need to follow to avoid some common problems and integrate them successfully. This article will guide you through some key best practices that I&rsquo;ve come across.</p>
<h2 id="understanding-the-challenges-of-implementing-llms-in-real-world-applications">Understanding the Challenges of Implementing LLMs in Real-World Applications</h2>
<p>One of the first challenges is that LLMs are constantly being improved. This means that the model you start using could change under the hood, and suddenly your application doesn&rsquo;t work as it did before. Your prompts might need adjustments to work with the newer version, or worse, they might even lead to unintended results!</p>
<p>Furthermore, you need to consider how to effectively manage your prompts, especially when your applications start to get more complex. Prompts can easily become convoluted and difficult to modify. Imagine having to comb through a hundred lines of code in your application to find a specific prompt just to tweak a single word. That&rsquo;s why <strong>prompt externalization</strong> (not keeping your prompts inside your source files) is going to be important, to easily find your prompts, and have a birds-eye view of all of them!</p>
<p>Keeping track of changes and ensuring that the LLM&rsquo;s behavior remains consistent throughout your development process is another challenge. How can you ensure that a particular feature still functions correctly after upgrading your prompts, or even changing model versions? You need to <strong>version your prompts</strong> (we&rsquo;ll cover that in more details in a moment). Think of your prompts like code — just like your software code, prompts should have version control for easy management. Versioning ensures that you can quickly revert to previous versions if necessary, while providing a helpful audit trail to see exactly what changes have occurred in your prompt management process.</p>
<h2 id="prompt-engineering-for-consistent-and-effective-llm-applications">Prompt Engineering for Consistent and Effective LLM Applications</h2>
<p>You&rsquo;ve probably noticed that one of the main things that determines how well your application works with a Large Language Model (LLM) is the <em>prompt</em> you use to guide it. Prompts act like a guidebook for the LLM, explaining what you expect from it and how it should format its response. You&rsquo;ve likely heard about the importance of using good prompts, but how do you go about creating prompts that are reliable and adaptable in the long run?</p>
<p>Think of your prompts like code artifacts. Just as you version your code to keep track of changes and ensure consistency, you should also version your prompts. This allows you to:</p>
<ul>
<li><strong>Keep track of your prompt evolution:</strong> You&rsquo;ll have a clear record of how your prompts have changed over time, which helps you understand the application&rsquo;s evolving behavior.</li>
<li><strong>Create a helpful audit trail:</strong> Having versions of your prompts will help you understand exactly how the application behaved at specific times. This is essential for debugging, diagnosing issues, or understanding how user feedback impacted your application.</li>
<li><strong>Enable rollbacks:</strong> If you encounter an issue or want to test different prompt versions, you can easily revert to a previous state to ensure that you can isolate problems, revert to previously working versions, or simply experiment with different phrasing.</li>
</ul>
<p>But simply versioning prompts isn&rsquo;t enough. Imagine you need to make a change to one particular prompt in a massive LLM-powered application. It might involve a lot of tedious code-hunting. That&rsquo;s where <strong>prompt externalization</strong>, that we mentioned earlier, comes in! Externalizing prompts is all about taking them out of your code and treating them like a separate configuration file. This way, they are:</p>
<ul>
<li><strong>Easy to modify:</strong> Changing your prompts becomes a breeze. Just go to your external prompt file, make the adjustments, and you&rsquo;re ready to go! No more scouring through complex code to find a single prompt in some string variables somewhere.</li>
<li><strong>More flexible:</strong> By using externalized prompts, you can easily experiment with different versions or phrasing without rewriting your entire application. This lets you quickly adapt your prompts in response to user feedback or changes in your model.</li>
<li><strong>Easier to manage:</strong> Keeping prompts in their own dedicated file makes it easy to maintain them, making sure that your prompts are consistent and up-to-date. This approach becomes increasingly valuable as your applications become more complex and you have a growing set of prompts to maintain.</li>
</ul>
<p>There are open source projects, or open formats that have emerged recently, to externalize prompts.
For examples Firebase&rsquo;s <a href="https://firebase.google.com/docs/genkit">GenKit</a> LLM framework came up with their <a href="https://firebase.google.com/docs/genkit/dotprompt">dotPrompt</a> format,
which not only externalizes the prompt itself, but also the name of the model, its configuration (temperature, etc.)</p>
<h2 id="model-versioning-preventing-surprises">Model Versioning: Preventing Surprises</h2>
<p>Now let&rsquo;s cover the importance of managing model versions, a critical aspect of ensuring that your LLM-powered application continues to work reliably.</p>
<p>Imagine that you&rsquo;ve built a great application using a particular model, and you&rsquo;re proud of the results. However, what happens when the LLM provider releases an updated version? It might offer performance improvements, but the updates can also change how the model responds to your prompts, potentially leading to unexpected issues or even breaking your application.</p>
<p>To avoid these unexpected changes, the key principle is to <strong>pin the specific version of the LLM model</strong> that you use for your application. For example, when using Gemini 1.5 Pro, if you use the version <code>gemini-1.5-pro</code>, you&rsquo;re actually using the latest version of the model. Currently, it&rsquo;s <code>gemini-1.5-pro-001</code>. But if tomorrow Google releases <code>gemini-1.5-pro-002</code>, your application would suddenly start using that new version. So be very explicit in the model version.</p>
<p>Here&rsquo;s why this is essential:</p>
<ul>
<li><strong>Avoid Drifting Model Behavior:</strong> The update to an LLM might come with subtle changes that can lead to a shift in the model&rsquo;s responses, and you may not always be able to anticipate these changes beforehand. This can lead to inconsistency, where a prompt that generated a certain output in one version of the model generates a completely different (and perhaps undesirable) output in a newer version.</li>
<li><strong>Maintain Application Consistency:</strong> To keep your application performing reliably, you want to control the LLM’s responses as much as possible, and pinning the model version ensures that you can do this. If you&rsquo;re using a specific model, the prompts that are part of your application work in the context of that model&rsquo;s specific training and behaviors. Pinning the version helps you avoid unexpected changes that may interfere with your prompts&rsquo; effectiveness.</li>
<li><strong>Simplify Auditing and Debugging:</strong> In case of an unexpected issue or an unexplained change in your LLM&rsquo;s behavior, being able to easily trace back the specific model version that&rsquo;s running provides invaluable context for debugging and understanding why those changes occurred. It helps isolate issues to specific model versions, so you can resolve them quicker.</li>
</ul>
<p>While using the latest and greatest LLM version might seem tempting for its improved capabilities, remember: <strong>the consistent performance and reliability of your application should be a top priority.</strong> By pinpointing the model version you use, you gain better control over its behavior and maintain a smooth and predictable experience for your users.</p>
<h2 id="optimizing-for-efficiency-the-power-of-response-caching">Optimizing for Efficiency: The Power of Response Caching</h2>
<p>Even with well-crafted prompts, pinned versions, generating responses from a Large Language Model (LLM) can still be expensive. This is where <strong>response caching</strong> comes in, offering a crucial way to improve both the performance and the cost-efficiency of your application.</p>
<p>Models like <a href="https://deepmind.google/technologies/gemini/">Gemini</a> support <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/context-cache/context-cache-overview">context caching</a>. Quoting the documentation:</p>
<blockquote>
<p>Use context caching to reduce the cost of requests that contain repeat content with high input token counts. Cached context items, such as a large amount of text, an audio file, or a video file, can be used in prompt requests to the Gemini API to generate output. Requests that use the same cache in the prompt also include text unique to each prompt. For example, each prompt request that composes a chat conversation might include the same context cache that references a video along with unique text that comprises each turn in the chat. The minimum size of a context cache is 32,768 tokens.</p></blockquote>
<p>By <strong>caching frequently used responses</strong> or heavy multimodal documents, you avoid having to generate them over and over again, leading to a dramatic improvement in performance and a reduction in LLM usage costs. Imagine users frequently asking the same question, like “What are the benefits of using your app?&quot;. By caching the response to this question, you&rsquo;ll be able to provide users with a fast and efficient response without burdening the LLM each time.</p>
<p>But how do you actually implement caching? You can choose different strategies for your caching system, each with its own benefits:</p>
<ul>
<li><strong>Context Caching:</strong> If your model, like Gemini, supports caching already, be sure to understand how it works, what can be cached or not, the pros and cons, or potential limitations.</li>
<li><strong>Basic Caching:</strong> Store LLM responses based on the exact input. If you encounter a query that you&rsquo;ve already generated, you can provide a pre-cached response, saving on processing time. You could also do some minimal string modifications to normalize whitespace, put everything in lowercase, etc, to get the chance to cache very similar prompts.</li>
<li><strong>Advanced Caching with Similarity Search:</strong> Even with string normalization, you might find that users don&rsquo;t always ask the exact same question, but the query can still be extremely similar. Think of typos, minor word substitutions, synonyms, or variations in punctuation. Instead of treating every query as unique, consider <strong>approximate nearest neighbor search</strong> and <strong>embedding vector similarity</strong>. This approach helps you find queries that are nearly identical, even with minor variations. You can then leverage this functionality to serve the same cached response for queries that are semantically similar, increasing the effectiveness of your caching strategy and ensuring that you only compute distinct queries once. For vector similarity, make sure to test with different inputs, to find the right threshold to say that a new prompt is equivalent to an older cached prompt/response pair.</li>
</ul>
<p>Caching responses not only speeds up your LLM-powered application, lowering the perceived latency, but also significantly cuts down on LLM usage costs, helping you keep your application running smoothly while maximizing cost-effectiveness.</p>
<h2 id="building-safeguards-ensuring-robustness-with-guardrails">Building Safeguards: Ensuring Robustness with Guardrails</h2>
<p>Let&rsquo;s shift our focus to building safety mechanisms. This is crucial for creating reliable, trustworthy applications. Enter the concept of <strong>guardrails</strong>, which are safety systems designed to protect your application and users from unexpected, unwanted, or even harmful outcomes.</p>
<p>Think of guardrails like a protective fence, ensuring that the LLM stays within safe boundaries while performing its tasks. Imagine if someone tried to make an inappropriate request, or worse, a request that could cause harm. This is where guardrails step in.</p>
<p>Guardrails serve two main purposes:</p>
<ul>
<li><strong>Input Validation:</strong> Guardrails can examine the user input and determine whether it&rsquo;s acceptable for your application and whether it aligns with your intended use case. Imagine preventing your LLM from processing prompts with malicious language or data that could cause harm to users.</li>
<li><strong>Output Filtering:</strong> Guardrails are not only for examining the user&rsquo;s input but also for checking the outputs of the LLM. By analyzing the LLM&rsquo;s generated responses, you can filter out inappropriate content or responses that don&rsquo;t meet your requirements.</li>
</ul>
<p>What are the three primary types of guardrails?</p>
<ul>
<li><strong>Model&rsquo;s safety settings:</strong> Models have usually been fine tuned to avoid certain harmful content in both input and output. They also give you access to safety settings, with different harm categories and safety thresholds. You should test those settings and how they can be configured for your use case. For example, have a look at the available <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/configure-safety-filters">safety filters</a> for Gemini.</li>
<li><strong>Static Guardrails:</strong> These are predefined rules that are set before the LLM begins to process any input. These can be rules that dictate certain formatting for prompts, limitations on input length, or even basic checks for prohibited terms or requests. Static guardrails offer fast processing times, since the checks are performed beforehand on the input strings, in your own code.</li>
<li><strong>Dynamic Guardrails:</strong> These are flexible guardrails that work in conjunction with the LLM or the embedding model used for text classification, continuously adapting to changes in user input or the output of the model itself. They allow you to handle complex or unpredictable situations and perform nuanced checks to maintain the safety and integrity of your application. You might have a look at Google Cloud Natural Language Processing&rsquo;s <a href="https://cloud.google.com/natural-language/docs/moderating-text">moderation endpoint</a>, or the free <a href="https://perspectiveapi.com/">Perspective API</a> used by newspapers.</li>
</ul>
<p>When you implement guardrails, it&rsquo;s also critical to consider performance impact. You want to make sure that these safeguards don&rsquo;t add too much latency and delay user experience. That&rsquo;s where <strong>parallel processing</strong> can come into play! Instead of waiting for the guardrail check to finish before starting the LLM generation, consider launching both tasks in parallel, optimizing speed and efficiency without sacrificing safety. Then, if the guardrails raise a red flag, you can stop the response generation, and reply right away to the user that the input content was problematic. For the response, unless you have a streaming kind of guardrail system, you might have to wait for the whole response to be generated before evaluating it with the guardrail, in which case, you can&rsquo;t really do parallel processing.</p>
<p>Always remember: Guardrails should be continuously refined and updated as you identify new potential risks. Gathering feedback from users, giving them the ability to report a bad response is one approach. But you should also monitor your application LLM responses to do some vibe-checks at random to ensure your application is behaving correctly.</p>
<h2 id="evaluating-and-monitoring-for-consistent-performance">Evaluating and Monitoring for Consistent Performance</h2>
<p>Onto the most crucial aspects of any application, regardless of its technology, is <strong>evaluation and monitoring</strong>. This is essential for ensuring your LLM-powered application continues to function reliably and meets your expectations as it interacts with users in the real world.</p>
<p>Imagine you make an update to your application, or perhaps even a simple tweak to one of your prompts. Without proper monitoring, you won&rsquo;t know if those changes had unintended consequences. You could end up with an app that gives unexpected results, leads to user frustration, or even creates unforeseen safety issues. That&rsquo;s where a robust evaluation and monitoring framework comes into play!</p>
<p>Your LLM-powered app needs a systematic way to ensure that everything is running smoothly and effectively. You need to:</p>
<ul>
<li>
<p><strong>Establish Evaluation Metrics:</strong> You need clear guidelines to judge the LLM&rsquo;s performance. Think of key metrics like accuracy, relevance, and coherence.</p>
<ul>
<li><strong>Accuracy:</strong> This measures how often the LLM generates correct and factually accurate responses. This is particularly crucial if your application is designed for providing reliable information or carrying out fact-based tasks.</li>
<li><strong>Relevance:</strong> You need to make sure the LLM stays focused on the core issue. It should respond to your prompts in a meaningful and helpful way, instead of giving irrelevant or off-topic responses.</li>
<li><strong>Coherence:</strong> You need to check if the LLM produces well-written and logical text. Coherent responses are easily understood by users and don&rsquo;t leave them feeling confused or disoriented.</li>
</ul>
</li>
<li>
<p><strong>Gather User Feedback:</strong> It&rsquo;s essential to go beyond just numbers. Your application&rsquo;s performance shouldn&rsquo;t just be evaluated on your own terms. Get feedback from the users, gather data on how they are using the application, and check their satisfaction with the outputs of your application. You can even ask users to provide their opinions on specific generated answers, giving you valuable insights into what resonates with them and how you can improve. Consider using tools like &ldquo;thumbs up&rdquo; or &ldquo;thumbs down&rdquo; buttons, offering an easy way for users to indicate their sentiment towards the LLM&rsquo;s responses, or a way to report and explain what wasn&rsquo;t up to the level of their expectations.</p>
</li>
<li>
<p><strong>Build a “Golden Responses” Dataset:</strong> Create a collection of carefully chosen inputs and their desired, accurate responses. These “golden” examples act like benchmarks, helping you measure how closely the LLM matches your expected results for specific tasks. By periodically checking how your LLM performs against these golden examples, you can get a clear picture of potential issues and make necessary adjustments. You can use this set as a starting point to track potential regressions and make sure the LLM&rsquo;s behavior is aligned with your expectations.</p>
</li>
<li>
<p><strong>Implement Continuous Monitoring:</strong> Monitoring shouldn&rsquo;t be a one-time event. It&rsquo;s an ongoing process, like keeping a watchful eye on your application as it functions in the real world. By monitoring in real-time, you can detect anomalies, unexpected issues, or performance regressions promptly. It allows you to address these issues before they cause significant problems for your users. Maybe checkout the recent <a href="https://opentelemetry.io/docs/specs/semconv/gen-ai/">OpenTelemetry guidelines for Gen AI</a> to observe how your system and LLM are performing live.</p>
</li>
</ul>
<p>You can further improve your LLM-powered application by analyzing the user&rsquo;s requests and responses generated by the LLM, especially those flagged by users as problematic or unexpected. These can be added to your collection of golden responses, constantly refining the process of evaluation. This helps your application evolve based on real-world interactions.</p>
<h2 id="addressing-data-privacy-concerns">Addressing Data Privacy Concerns</h2>
<p>Another important topic to keep in mind: <strong>data privacy</strong>. LLMs have access to a vast amount of text data, which makes them incredibly powerful. But this same power brings with it the responsibility of safeguarding sensitive information. If your application handles user data, you need to ensure that you&rsquo;re handling it with utmost care, protecting it from unauthorized access and ensuring that you comply with relevant privacy regulations.</p>
<p>Think of data privacy as a trust contract. You, as the developer, are entrusted with safeguarding the sensitive information of your users. It&rsquo;s your responsibility to implement measures that keep this data secure and prevent breaches or misuse.</p>
<p>Here are some key steps to address data privacy concerns in your LLM application:</p>
<ul>
<li><strong>Implement strong security measures:</strong> Use robust encryption methods to secure your application and data. Employ security best practices such as access controls, secure storage, and secure communication channels.</li>
<li><strong>Stay aligned with data privacy regulations:</strong> Comply with relevant privacy regulations like GDPR, CCPA, and HIPAA. You might need to review your data handling policies and make necessary adjustments.</li>
<li><strong>Ensure data anonymization:</strong> When working with sensitive data, always strive to anonymize or pseudonymize it to the fullest extent possible. You can utilize techniques like differential privacy, aggregation, or removing identifying details to protect user information (with <a href="https://cloud.google.com/security/products/dlp">Google Cloud Data Loss Prevention</a> API for example).</li>
<li><strong>Be transparent with users:</strong> Communicate clearly with your users about how you collect, use, and store their data. Offer users options to control their data, and provide mechanisms to update or delete their information if needed.</li>
</ul>
<p>By prioritizing data privacy in your LLM application, you not only uphold ethical standards but also build trust with your users. Your users should be confident that their information is being handled with respect and care, encouraging long-term trust in your application.</p>
<h2 id="tailoring-llms-for-specific-business-goals">Tailoring LLMs for Specific Business Goals</h2>
<p>Remember that LLMs are tools, and the success of your LLM application ultimately hinges on aligning its capabilities with your unique goals and your target audience. So, how do you get the most out of an LLM in your business?</p>
<p><strong>First, define your goals.</strong> What specific tasks can an LLM help you accomplish? What pain points are you trying to solve? Once you understand the big picture, you can break down those goals into actionable tasks that the LLM can potentially assist with.</p>
<p><strong>Then, it&rsquo;s time to find the right LLM for the job.</strong> Not all LLMs are created equal. Different models excel at specific tasks, have varying levels of language support, and even require different levels of computational resources. For example, if your business uses many different languages, you’ll want an LLM with multilingual support.</p>
<p>To select the best LLM for your needs, ask yourself:</p>
<ul>
<li><strong>What specific task does this LLM need to perform?</strong> Different LLMs excel at different tasks like text generation, summarization, or translation.</li>
<li><strong>How does the LLM&rsquo;s accuracy compare with the level of accuracy required for your application?</strong> The model needs to generate results with the appropriate level of precision for your goals.</li>
<li><strong>How much computational power does it need to run this LLM?</strong> Consider your budget and available infrastructure when making this selection, when hosting the model on your own. A cloud hosted model might be better (and cheaper) depending on your usage patterns, and if you don&rsquo;t want the hassle to handle your own infrastructure and GPUs.</li>
<li><strong>What language capabilities does the LLM offer?</strong> Is the model good at the languages you need to use, or are there specific domains where the model is particularly strong? It&rsquo;s not just about spoken languages, with code as well, some models maybe better dealing with a particular programming language than another one.</li>
</ul>
<p>You can often find models with specialized skills. You may find, for example, a model trained on scientific papers if your work requires the processing of highly technical content, or a model trained on a particular field, such as text of laws, to be highly effective in that domain.</p>
<p>Once you’ve chosen your LLM, the next step could be <strong>fine-tuning</strong>, where you’d tailor the model to your specific needs. It’s like customizing a tool to do the exact job you need it to do. For example, imagine your application is helping people book vacations. You can train the model on a massive amount of vacation-related text data so it can accurately understand and respond to vacation-specific questions, making your application highly relevant for its intended purpose. But fine-tuning is not necessarily for the faint of heart, and can be complicated to do right.</p>
<p>While choosing and fine-tuning are critical steps, <strong>assessing potential risks is equally important.</strong> Think about potential unintended consequences. LLMs, for example, might not always be factual or accurate in their responses. You&rsquo;ll need to find ways to manage those potential issues, often incorporating guardrails to mitigate potential harms or biases, or implementing techniques like Retrieval Augmented Generation to ground the model&rsquo;s responses on your own data and documents.</p>
<p>Ultimately, you&rsquo;ll want to make your application a tool that not only works reliably but also gives real value to your business. By understanding your business goals, choosing the right model, customizing it effectively, and understanding the potential risks, you’re on the right path to success!</p>
<h2 id="looking-ahead-emerging-trends-and-future-directions">Looking Ahead: Emerging Trends and Future Directions</h2>
<p>Remember that this field is constantly changing! New capabilities are emerging, and existing models are getting even smarter and more efficient. This is an exciting time to be working with LLMs because the possibilities feel endless!</p>
<p>While it&rsquo;s fantastic to get your application off the ground using the latest LLMs, it&rsquo;s equally important to be open to continuous improvement. What&rsquo;s great today may not be optimal in the future. The world of LLMs is one where ongoing development is key! Here are a few tips:</p>
<ul>
<li><strong>Embrace continuous learning.</strong> You should always be seeking out information about the newest developments in the field, how LLMs are being enhanced, and the impact those changes could have on your applications. Look out for improvements to existing models, new LLM models coming out, and fresh research.</li>
<li><strong>Think ahead.</strong> What new features could you integrate in your application that take advantage of those advancements? Would your app benefit from a specific, task-oriented model that focuses on summarization, question answering, or code generation? Maybe there&rsquo;s a model out there that will significantly boost performance and help you offer a smoother, more feature-rich experience for your users!</li>
<li><strong>Prepare for evolution.</strong> Remember that LLMs aren’t static! Your app should be built with a framework for easy adaptation. Consider how you can adapt to model updates or new model releases in a structured way, perhaps by putting in place frameworks for incorporating new models seamlessly and managing prompt changes for various models, like <a href="https://docs.langchain4j.dev/">LangChain4j</a> (if you&rsquo;re a Java developer) which offers higher-level abstractions and that allows you to switch models easily.</li>
</ul>
<p>The landscape of LLMs is evolving rapidly. Stay up-to-date with the latest developments and ensure your applications can adapt, allowing you to unlock the full potential of LLMs for your business!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>New Gemini model in LangChain4j</title><link>https://glaforge.dev/posts/2024/09/05/new-gemini-model-in-langchain4j/</link><pubDate>Thu, 05 Sep 2024 22:42:38 +0200</pubDate><guid>https://glaforge.dev/posts/2024/09/05/new-gemini-model-in-langchain4j/</guid><description>&lt;p>A new version of &lt;a href="https://docs.langchain4j.dev/">LangChain4j&lt;/a>, the super powerful LLM toolbox for Java developers, was released today.
In &lt;a href="https://github.com/langchain4j/langchain4j/releases">0.34.0&lt;/a>, a new Gemini model has been added.
This time, this is not the Gemini flavor from Google Cloud Vertex AI, but the &lt;a href="https://ai.google.dev/gemini-api/">Google AI&lt;/a> variant.&lt;/p>
&lt;p>It was a frequently requested feature by LangChain4j users,
so I took a stab at developing a new chat model for it, during my summer vacation break.&lt;/p>
&lt;h2 id="gemini-show-me-the-code">Gemini, show me the code!&lt;/h2>
&lt;p>Let&amp;rsquo;s dive into some code examples to see it in action!&lt;/p></description><content:encoded>
<![CDATA[<p>A new version of <a href="https://docs.langchain4j.dev/">LangChain4j</a>, the super powerful LLM toolbox for Java developers, was released today.
In <a href="https://github.com/langchain4j/langchain4j/releases">0.34.0</a>, a new Gemini model has been added.
This time, this is not the Gemini flavor from Google Cloud Vertex AI, but the <a href="https://ai.google.dev/gemini-api/">Google AI</a> variant.</p>
<p>It was a frequently requested feature by LangChain4j users,
so I took a stab at developing a new chat model for it, during my summer vacation break.</p>
<h2 id="gemini-show-me-the-code">Gemini, show me the code!</h2>
<p>Let&rsquo;s dive into some code examples to see it in action!</p>
<p>But first, you&rsquo;ll need an API key.
So just follow the instructions to <a href="https://ai.google.dev/gemini-api/docs/api-key">obtain your Gemini API key</a>.
I&rsquo;ve saved mine in the <code>GEMINI_AI_KEY</code> environment variable,
so that I don&rsquo;t have to hardcode it in my source files.</p>
<p>The code examples below have been compiled with Java 21.</p>
<p>I&rsquo;ve imported the following libraries in my build tool:</p>
<ul>
<li><code>dev.langchain4j:langchain4j-google-ai-gemini:0.34.0</code></li>
<li><code>dev.langchain4j:langchain4j-core:0.34.0</code></li>
<li><code>dev.langchain4j:langchain4j:0.34.0</code></li>
</ul>
<h3 id="lets-be-polite-and-say-hello">Let&rsquo;s be polite and say hello</h3>
<p>My mom always told me to be polite and to say hello:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ChatLanguageModel<span style="color:#bbb"> </span>gemini<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_AI_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>gemini.<span style="color:#4070a0">generate</span>(<span style="color:#4070a0">&#34;Konnichiwa Gemini!&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;Gemini&gt; &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>response);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Gemini&gt; Konnichiwa! It&#39;s nice to hear from you.</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//         What can I do for you today?</span><span style="color:#bbb">
</span></span></span></code></pre></div><h3 id="dont-you-like-strawberries">Don&rsquo;t you like strawberries?</h3>
<p>In the first example, I used the usual <code>generate()</code> method to send my greeting to Gemini.
But LangChain4j 0.34 introduces some new signatures and classes to interact with an LLM:</p>
<ul>
<li><code>ChatRequest</code>: a new class that contains your conversation messages,
the tools this request can use, and a response format definition to decide
what should be the shape of the output</li>
<li><code>ChatResponse</code>: this class holds the LLM&rsquo;s response, the token usage information,
and the <em>finish</em> reason (ie. if the response was cut, filtered, or was generated till the end)</li>
<li><code>ChatResponse chat(ChatRequest req)</code>: this new method is added to the LLM contract to interact with it.</li>
</ul>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ChatLanguageModel<span style="color:#bbb"> </span>gemini<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_AI_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ChatResponse<span style="color:#bbb"> </span>chatResponse<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>gemini.<span style="color:#4070a0">chat</span>(ChatRequest.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">messages</span>(UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;How many R&#39;s are there in the word &#39;strawberry&#39;?&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>chatResponse.<span style="color:#4070a0">aiMessage</span>().<span style="color:#4070a0">text</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;Gemini&gt; &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>response);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Gemini&gt; There are **three** R&#39;s in the word &#34;strawberry&#34;.</span><span style="color:#bbb">
</span></span></span></code></pre></div><h3 id="lets-roll-the-json-dice">Let&rsquo;s roll the JSON dice!</h3>
<p>Both Gemini 1.5 Flash and Pro allow you to specify that the output should be valid JSON.
It&rsquo;s sometimes called the <strong>JSON mode</strong>.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ChatLanguageModel<span style="color:#bbb"> </span>gemini<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_AI_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseMimeType</span>(<span style="color:#4070a0">&#34;application/json&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>roll<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>gemini.<span style="color:#4070a0">generate</span>(<span style="color:#4070a0">&#34;Roll a 6-sided dice&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(roll);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// {&#34;roll&#34;: &#34;3&#34;}</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>Gemini will always reply with valid JSON structures.</p>
<p>Here, the JSON object key is not always <code>roll</code>, and is sometimes <code>die</code>, <code>dice_roll</code>, etc.
But you could tweak your prompt to ask for a specific key name.</p>
<p>Gemini follows the instructions very precisely, but it&rsquo;s not guaranteed 100% that it will really use the requested key name.
But fear not, there&rsquo;s an even more powerful solution, thanks to response formats!</p>
<h3 id="lets-cook-something-with-our-strawberries">Let&rsquo;s cook something with our strawberries</h3>
<p>You can configure Gemini to make it generate outputs that comply with a JSON schema.
It&rsquo;s sometimes called <strong>controlled generation</strong>, or <strong>constrained decoding</strong>.</p>
<p>Let&rsquo;s say we have a schema that represents recipes!
It&rsquo;s time to do something with our strawberries!</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ChatLanguageModel<span style="color:#bbb"> </span>gemini<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_AI_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseSchema</span>(JsonSchema.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">rootElement</span>(JsonObjectSchema.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">properties</span>(Map.<span style="color:#4070a0">of</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#4070a0">&#34;title&#34;</span>,<span style="color:#bbb"> </span>JSON_STRING_SCHEMA,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#4070a0">&#34;preparationTimeMinutes&#34;</span>,<span style="color:#bbb"> </span>JSON_INTEGER_SCHEMA,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#4070a0">&#34;ingredients&#34;</span>,<span style="color:#bbb"> </span>JsonArraySchema.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">items</span>(JSON_STRING_SCHEMA)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">build</span>(),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#4070a0">&#34;steps&#34;</span>,<span style="color:#bbb"> </span>JsonArraySchema.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">items</span>(JSON_STRING_SCHEMA)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">build</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>recipeResponse<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>gemini.<span style="color:#4070a0">generate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;Suggest a dessert recipe with strawberries&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(recipeResponse);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">/*
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">{&#34;ingredients&#34;: [&#34;1 pint fresh strawberries, hulled and sliced&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">&#34;1/2 cup sugar&#34;, &#34;1/4 cup water&#34;, &#34;1 tablespoon lemon juice&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">&#34;1/2 teaspoon vanilla extract&#34;, &#34;1 cup heavy cream, whipped&#34;],
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">&#34;preparationTimeMinutes&#34;: 30, &#34;steps&#34;: [&#34;In a saucepan, combine
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">the sugar, water, and lemon juice. Bring to a boil over medium
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">heat, stirring until the sugar is dissolved.&#34;, &#34;Reduce the heat
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">to low and simmer for 5 minutes, or until the syrup thickens
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">slightly.&#34;, &#34;Remove from heat and stir in the vanilla extract.&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">&#34;Pour the syrup over the strawberries in a bowl and stir to coat.&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">&#34;Refrigerate for at least 30 minutes, or until chilled.&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">&#34;To serve, top the strawberries with whipped cream and enjoy!&#34;],
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">&#34;title&#34;: &#34;Strawberry Shortcake&#34;}
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"> */</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>Gemini strictly follows the specified JSON schema, and generates a JSON object that matches.</p>
<p>This is particularly important when you integrate LLMs in your application.
You want a deterministic format for the output that can easily be parsed and handled by your system.</p>
<h3 id="tasty-strawberries-from-japan">Tasty strawberries from Japan!</h3>
<p>A few months back, I had the chance to visit Japan with my family,
and they have some really gorgeous and tasty strawberries there!
And don&rsquo;t get me started on strawberry daifukus (mochis with fruits inside) we had in Osaka!</p>
<p>But before tasting those lovely confections, we need to plan our trip to Japan.</p>
<p>In the previous example, you might have found that a bit painful to describe the JSON schema.
For the integration in a Java application, you might have some more complex data structures to represent,
so deriving the big schema to define them can be tedious.
Fortunately, there&rsquo;s a little trick to get the JSON schema for a Java class (or record, enum, etc.).</p>
<p>Let&rsquo;s define and describe our trip itinerary object:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Description</span>(<span style="color:#4070a0">&#34;details of a trip itinerary&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">TripItinerary</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>country,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Integer<span style="color:#bbb"> </span>numberOfPersons,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Month<span style="color:#bbb"> </span>month,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Description</span>(<span style="color:#4070a0">&#34;key highlights when visiting the city&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>List<span style="color:#666">&lt;</span>CityHighlights<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>cityHighlights<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">enum</span><span style="color:#bbb"> </span>Month<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>JANUARY,<span style="color:#bbb"> </span>FEBRUARY,<span style="color:#bbb"> </span>MARCH,<span style="color:#bbb"> </span>APRIL,<span style="color:#bbb"> </span>MAY,<span style="color:#bbb"> </span>JUNE,<span style="color:#bbb"> </span>JULY,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>AUGUST,<span style="color:#bbb"> </span>SEPTEMBER,<span style="color:#bbb"> </span>OCTOBER,<span style="color:#bbb"> </span>NOVEMBER,<span style="color:#bbb"> </span>DECEMBER<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">CityHighlights</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>cityName,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>List<span style="color:#666">&lt;</span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>visitHighlights<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb"> </span>{<span style="color:#bbb"> </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>The itinerary is represented by some records, enums, and lists,
and the <code>@Description</code> annotation can help the LLM to better understand what some elements might be about
(in particular when you have some cryptic field names, but here,
it&rsquo;s not strictly necessary as Gemini is smart enough to understand what each field is about)</p>
<p>Now let&rsquo;s ask for our Japan itinerary:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ChatLanguageModel<span style="color:#bbb"> </span>gemini<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_AI_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">temperature</span>(2.<span style="color:#4070a0">0</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseSchema</span>(jsonSchemaFrom(TripItinerary.<span style="color:#4070a0">class</span>).<span style="color:#4070a0">get</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>We derive the JSON schema from the <code>TripItinerary</code> class.
No need to tediously craft a JSON schema for it.</p>
<p>Let&rsquo;s see what Gemini suggests for our visit:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>tripResponse<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>gemini.<span style="color:#4070a0">generate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>SystemMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;You are an expert trip planner&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Suggest an itinerary for Japan.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Cities visited: Tokyo, Kyoto, Osaka.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Trip for a family of 4 persons.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Provide key highlights for each city visited.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>We tell Gemini to act as an expert trip planner, and we give some details about the travellers, the cities we&rsquo;d like to visit.</p>
<p>So what&rsquo;s the JSON structured output for this Japan visit?</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(tripResponse);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">/*
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">{&#34;cityHighlights&#34;: [{&#34;cityName&#34;: &#34;Tokyo&#34;, &#34;visitHighlights&#34;:
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">[&#34;Explore the vibrant Shibuya Crossing and the iconic Shibuya
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">Scramble.&#34;, &#34;Visit the Meiji Jingu Shrine, a serene oasis in
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">the heart of Tokyo.&#34;, &#34;Experience the fascinating world of
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">technology at the Miraikan National Museum of Emerging Science
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">and Innovation.&#34;, &#34;Enjoy a traditional tea ceremony at one of
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">Tokyo&#39;s many teahouses.&#34;, &#34;Get lost in the eclectic streets
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">of Harajuku and admire the unique fashion styles.&#34;, &#34;Embark
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">on a scenic boat trip on the Sumida River, passing by Tokyo
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">Skytree.&#34;, &#34;Indulge in a delightful sushi dinner at a renowned
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">Tsukiji Fish Market.&#34;, &#34;Discover the charm of Ueno Park, home
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">to museums, temples, and the Ueno Zoo.&#34;]}, {&#34;cityName&#34;: &#34;Kyoto&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">&#34;visitHighlights&#34;: [&#34;Wander through the serene gardens of the
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">Golden Pavilion (Kinkaku-ji).&#34;, &#34;Immerse yourselves in the rich
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">history of the Kiyomizu-dera Temple, famous for its wooden stage.&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">&#34;Explore the ancient Gion district, known for its traditional
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">wooden buildings and geisha houses.&#34;, &#34;Stroll through the Fushimi
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">Inari Shrine, famous for its thousands of red torii gates.&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">&#34;Discover the treasures of the Nishiki Market, offering a diverse
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">selection of food and crafts.&#34;, &#34;Experience a traditional geisha
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">performance at one of Kyoto&#39;s exclusive theaters.&#34;, &#34;Learn the
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">art of calligraphy at a traditional workshop in the Gion district.&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">&#34;Relax in the serene atmosphere of the Ryoan-ji Zen Garden.&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">&#34;Witness the beauty of the Arashiyama Bamboo Grove.&#34;]},
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">{&#34;cityName&#34;: &#34;Osaka&#34;, &#34;visitHighlights&#34;: [&#34;Experience the vibrant
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">Dotonbori district, renowned for its neon lights, street food,
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">and entertainment.&#34;, &#34;Explore the Osaka Castle, a historic
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">landmark and symbol of the city.&#34;, &#34;Enjoy the breathtaking
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">panoramic views from the Abeno Harukas, Japan&#39;s tallest
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">skyscraper.&#34;, &#34;Visit the Osaka Aquarium Kaiyukan, home to diverse
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">marine life from around the world.&#34;, &#34;Stroll through the lively
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">Kuromon Market, known for its fresh seafood and local produce.&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">&#34;Take a scenic ride on the Osaka Ferris Wheel, offering views of
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">the cityscape.&#34;, &#34;Indulge in the delicious okonomiyaki, Osaka&#39;s
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">signature dish.&#34;, &#34;Experience the unique culture of the Sumiyoshi
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">Taisha Shrine, dedicated to the gods of seafaring.&#34;]}],
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">&#34;country&#34;: &#34;Japan&#34;, &#34;month&#34;: &#34;MARCH&#34;, &#34;numberOfPersons&#34;: 4}
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"> */</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>Damn! It didn&rsquo;t even mention the most delicious daifukus we had in Osaka!</p>
<h3 id="can-i-go-outside-without-my-umbrella-in-osaka-tonight">Can I go outside without my umbrella in Osaka, tonight?</h3>
<p>Speaking of visiting Osaka and those great daifukus, what&rsquo;s the weather like there?
It&rsquo;s been raining a lot in Paris today, so I&rsquo;m curious if it&rsquo;s better in Osaka.</p>
<p>This new Gemini chat model works with LangChain4j&rsquo;s higher-level abstractions: <code>AiServices</code>,
to create some very powerful LLM based apps, like smart agents or RAG (Retrieval Augmented Generation).</p>
<p>We&rsquo;ll have a look at a great use case for LLMs: <strong>data extraction from free-form text</strong>.</p>
<p>Let&rsquo;s define and describe a weather forecast record:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">WeatherForecast</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Description</span>(<span style="color:#4070a0">&#34;minimum temperature&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Integer<span style="color:#bbb"> </span>minTemperature,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Description</span>(<span style="color:#4070a0">&#34;maximum temperature&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Integer<span style="color:#bbb"> </span>maxTemperature,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Description</span>(<span style="color:#4070a0">&#34;chances of rain&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#902000">boolean</span><span style="color:#bbb"> </span>rain<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb"> </span>{<span style="color:#bbb"> </span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>We&rsquo;ll also create an interface for our weather service contract:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">interface</span> <span style="color:#0e84b5;font-weight:bold">WeatherForecastAssistant</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>WeatherForecast<span style="color:#bbb"> </span><span style="color:#06287e">extract</span>(String<span style="color:#bbb"> </span>forecast);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Let&rsquo;s configure Gemini, instantiate our weather assistant,
and extract the weather forecast from today&rsquo;s newspaper:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ChatLanguageModel<span style="color:#bbb"> </span>gemini<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_AI_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>WeatherForecastAssistant<span style="color:#bbb"> </span>forecastAssistant<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>AiServices.<span style="color:#4070a0">builder</span>(WeatherForecastAssistant.<span style="color:#4070a0">class</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">chatLanguageModel</span>(gemini)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>WeatherForecast<span style="color:#bbb"> </span>forecast<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>forecastAssistant.<span style="color:#4070a0">extract</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Morning: The day dawns bright and clear in Osaka, with crisp
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    autumn air and sunny skies. Expect temperatures to hover
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    around 18°C (64°F) as you head out for your morning stroll
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    through Namba.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Afternoon: The sun continues to shine as the city buzzes with
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    activity. Temperatures climb to a comfortable 22°C (72°F).
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Enjoy a leisurely lunch at one of Osaka&#39;s many outdoor cafes,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    or take a boat ride on the Okawa River to soak in the beautiful
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    scenery.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Evening: As the day fades, expect clear skies and a slight chill
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    in the air. Temperatures drop to 15°C (59°F). A cozy dinner at a
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    traditional Izakaya will be the perfect way to end your day in
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Osaka.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Overall: A beautiful autumn day in Osaka awaits, perfect for
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    exploring the city&#39;s vibrant streets, enjoying the local cuisine,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    and soaking in the sights.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Don&#39;t forget: Pack a light jacket for the evening and wear
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    comfortable shoes for all the walking you&#39;ll be doing.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;Gemini&gt; &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>forecast);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Gemini&gt; WeatherForecast[</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//             minTemperature=15,</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//             maxTemperature=22,</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//             rain=false]</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>Awesome, no need for my umbrella!</p>
<p>What&rsquo;s great here is that we&rsquo;re dealing with a real type-safe Java object, not JSON strings like before.
So it integrates very well within our Java codebase!</p>
<h3 id="time-for-a-little-coding-quiz">Time for a little coding quiz</h3>
<p>Alright, after the touristic detour, let&rsquo;s get back to some math, and some coding.
LLMs are quite good at reasoning, in particular when you encourage them to think <em>step by step</em>.
But sometimes, they fall short, and can&rsquo;t really calcuate results.
They&rsquo;re language models, not calculators, right?</p>
<p>Gemini has the ability to create some Python scripts, and to execute them in a sandbox.
So how can we configure Gemini for solving a little math problem?</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ChatLanguageModel<span style="color:#bbb"> </span>gemini<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_AI_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">allowCodeExecution</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">includeCodeExecutionOutput</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>There are 2 builder methods:</p>
<ul>
<li><code>allowCodeExecution(true)</code>: to let Gemini know it can do some Python coding</li>
<li><code>includeCodeExecutionOutput(true)</code>: if you want to see the actual Python script it came up with, and the output of its execution</li>
</ul>
<p>Do you know off head how much is <code>fibonacci(22)</code> or <code>ackermann(3, 4)</code>?
Let&rsquo;s ask Gemini:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>mathQuizz<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>gemini.<span style="color:#4070a0">generate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>SystemMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You are an expert mathematician.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        When asked a math problem or logic problem,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        you can solve it by creating a Python program,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        and execute it to return the result.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Implement the Fibonacci and Ackermann functions.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        What is the result of `fibonacci(22)` - ackermann(3, 4)?
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Looks like Gemini is a Python and math wiz:</p>
<pre tabindex="0"><code>Code executed:
```python
def fibonacci(n):
    if n &lt;= 1:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2)

def ackermann(m, n):
    if m == 0:
        return n + 1
    elif n == 0:
        return ackermann(m - 1, 1)
    else:
        return ackermann(m - 1, ackermann(m, n - 1))

print(fibonacci(22) - ackermann(3, 4))
```
Output:
```
17586
```
The result of `fibonacci(22) - ackermann(3, 4)` is **17586**.

I implemented the Fibonacci and Ackermann functions in Python.
Then I called `fibonacci(22) - ackermann(3, 4)` and printed the result.
</code></pre><p>If you don&rsquo;t include the script code and output, you would receive only the end of the message:</p>
<pre tabindex="0"><code>The result of `fibonacci(22) - ackermann(3, 4)` is **17586**.

I implemented the Fibonacci and Ackermann functions in Python.
Then I called `fibonacci(22) - ackermann(3, 4)` and printed the result.
</code></pre><p>I didn&rsquo;t encounter any snake in Japan, but I&rsquo;m happy Gemini can write some Python functions when needed!</p>
<h3 id="what-about-the-weather-in-tokyo">What about the weather in Tokyo?</h3>
<p>Besides this Python code execution sandbox, the more traditional <strong>function calling</strong> mechanism works.
We heard about the weather in Osaka, now let&rsquo;s ask for Tokyo.</p>
<p>Let&rsquo;s define a <em>tool</em> to retrieve structured weather forecasts:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">WeatherForecast</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>location,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>forecast,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>temperature)<span style="color:#bbb"> </span>{}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">WeatherForecastService</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Tool</span>(<span style="color:#4070a0">&#34;Get the weather forecast for a location&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>WeatherForecast<span style="color:#bbb"> </span><span style="color:#06287e">getForecast</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#555;font-weight:bold">@P</span>(<span style="color:#4070a0">&#34;Location to get the forecast for&#34;</span>)<span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>location)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(location.<span style="color:#4070a0">equals</span>(<span style="color:#4070a0">&#34;Paris&#34;</span>))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>WeatherForecast(<span style="color:#4070a0">&#34;Paris&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;sunny&#34;</span>,<span style="color:#bbb"> </span>20);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(location.<span style="color:#4070a0">equals</span>(<span style="color:#4070a0">&#34;London&#34;</span>))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>WeatherForecast(<span style="color:#4070a0">&#34;London&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;rainy&#34;</span>,<span style="color:#bbb"> </span>15);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(location.<span style="color:#4070a0">equals</span>(<span style="color:#4070a0">&#34;Tokyo&#34;</span>))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>WeatherForecast(<span style="color:#4070a0">&#34;Tokyo&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;warm&#34;</span>,<span style="color:#bbb"> </span>32);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>WeatherForecast(<span style="color:#4070a0">&#34;Unknown&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;unknown&#34;</span>,<span style="color:#bbb"> </span>0);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>We need a weather forecast assistant as well, that we&rsquo;ll instantiate and configure with our tool, thanks to <code>AiServices</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">interface</span> <span style="color:#0e84b5;font-weight:bold">WeatherAssistant</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span><span style="color:#06287e">chat</span>(String<span style="color:#bbb"> </span>userMessage);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>WeatherForecastService<span style="color:#bbb"> </span>weatherForecastService<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>WeatherForecastService();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ChatLanguageModel<span style="color:#bbb"> </span>gemini<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_AI_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">temperature</span>(0.<span style="color:#4070a0">0</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>WeatherAssistant<span style="color:#bbb"> </span>weatherAssistant<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>AiServices.<span style="color:#4070a0">builder</span>(WeatherAssistant.<span style="color:#4070a0">class</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">chatLanguageModel</span>(gemini)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">tools</span>(weatherForecastService)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>tokyoWeather<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>weatherAssistant.<span style="color:#4070a0">chat</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;What is the weather forecast for Tokyo?&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;Gemini&gt; &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>tokyoWeather);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// Gemini&gt; The weather forecast for Tokyo is warm</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//         with a temperature of 32 degrees.</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>I didn&rsquo;t expect such warm temperatures in Tokyo!
Well, of course, it&rsquo;s all fake, but you can imagine calling a real weather service.</p>
<p>The beauty of LangChain4j&rsquo;s <code>AiServices</code> is that it handles calling the service for you.
Usually, with function calling, the LLM just replies with a request that says <em>you</em> should be calling a tool or API,
and give it back the tool/API&rsquo;s answer.
Here, with <code>AiServices</code>, it&rsquo;s all automatic and transparent.</p>
<h3 id="of-parrots-pictures-text-files-and-multimodality">Of parrots pictures, text files, and multimodality</h3>
<p>Let&rsquo;s finish our whirlwind tour of this Google AI Gemini model for LangChain4j
with an example that highlights <strong>Gemini&rsquo;s multimodal capabilities</strong>.</p>
<p>Gemini is a multimodal LLM: in input, in addition to text, it accepts pictures, videos, audio, PDF files, and text files.</p>
<p>Let&rsquo;s ask what Gemini thinks of the cute colorful parrot mascot of LangChain4j.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// README.md markdown file from LangChain4j&#39;s project Github repos</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>base64Text<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>b64encoder.<span style="color:#4070a0">encodeToString</span>(readBytes(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#4070a0">&#34;https://github.com/langchain4j/langchain4j/blob/main/README.md&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// PNG of the cute colorful parrot mascot of the LangChain4j project</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>base64Img<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>b64encoder.<span style="color:#4070a0">encodeToString</span>(readBytes(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#4070a0">&#34;https://avatars.githubusercontent.com/u/132277850?v=4&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ChatLanguageModel<span style="color:#bbb"> </span>gemini<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GEMINI_AI_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>gemini.<span style="color:#4070a0">generate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>TextFileContent.<span style="color:#4070a0">from</span>(base64Text,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;text/x-markdown&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>ImageContent.<span style="color:#4070a0">from</span>(base64Img,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;image/png&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>TextContent.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            Do you think this logo fits well
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            with the project description?
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;Gemini&gt; &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>response);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">/*
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">   Gemini&gt; The logo of a parrot drinking tea doesn&#39;t seem like a
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">   good fit for a project description of a Java version of LangChain.
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">   It&#39;s not clear how the logo relates to the project&#39;s purpose or
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">   functionality. A logo that better reflects the project&#39;s technical
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">   nature, such as a stylized representation of code or a language
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">   model, would be more appropriate.
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"> */</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>Ah well, looks like LLM don&rsquo;t want to be compared to <em>stochastic parrots</em>,
so it thinks the parrot mascot doesn&rsquo;t represent the project well enough!</p>
<p>Sorry Gemini, I have to disagree, I really love this logo!</p>
<p>This example shows that you can craft an elaborate prompt that contains a text query,
an external text file (the description of the project in Markdown format),
and the picture of the parrot mascot.</p>
<h2 id="lets-wrap-up--with-beautiful-furoshiki-fabric">Let&rsquo;s wrap up — with beautiful &lsquo;furoshiki&rsquo; fabric!</h2>
<p>Throughout this journey through code examples, strawberries, daifukus, Japan itineraries and weather forecasts,
you learned about the brand new LangChain4j module for Google AI&rsquo;s Gemini API, and its capabilities.
I hope this article makes you want to try it out!</p>
<p>Before calling it a day or night (depending on your timezone), I&rsquo;d like to mention some limitations,
as it&rsquo;s still early days for this new module:</p>
<ul>
<li>Currently, there&rsquo;s only a <code>ChatLanguageModel</code> available,
but no <code>StreamingChatLanguageModel</code> class, so you won&rsquo;t get streamed responses yet.</li>
<li>Gemini&rsquo;s content caching capability is not surfaced in this implementation,
so you can&rsquo;t use caching to save some bucks or yens.</li>
<li>For multimodality, you should pass the Base64 encoded bytes of the files, and not use URLs to reference those resources,
as this module doesn&rsquo;t yet upload files to Gemini&rsquo;s file service (Gemini won&rsquo;t download from an external URL).</li>
</ul>
<p>Hopefully, the community will adopt this module, work with it, provide feedback to help us improve it further!
Don&rsquo;t hesitate to reach out with questions or to report any problems you encounter.
And if you build something cool, please tell me too!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A retryable JUnit 5 extension for flaky tests</title><link>https://glaforge.dev/posts/2024/09/01/a-retryable-junit-5-extension/</link><pubDate>Sun, 01 Sep 2024 16:00:51 +0200</pubDate><guid>https://glaforge.dev/posts/2024/09/01/a-retryable-junit-5-extension/</guid><description>&lt;p>As I work a lot with Large Language Models (LLMs), I often have to deal with flaky test cases,
because LLMs are not always consistent and deterministic in their responses.
Thus, sometimes, a test passes maybe a few times in a row, but then, once in a while, it fails.&lt;/p>
&lt;p>Maybe some prompt tweaks will make the test pass more consistently, lowering the temperature too,
or using techniques like few-shot prompting will help the model better understand what it has to do.
But in some circumenstances, you can&amp;rsquo;t find ways around those weird failures,
and the sole solution I found was to make a test &lt;em>retryable&lt;/em>.&lt;/p></description><content:encoded>
<![CDATA[<p>As I work a lot with Large Language Models (LLMs), I often have to deal with flaky test cases,
because LLMs are not always consistent and deterministic in their responses.
Thus, sometimes, a test passes maybe a few times in a row, but then, once in a while, it fails.</p>
<p>Maybe some prompt tweaks will make the test pass more consistently, lowering the temperature too,
or using techniques like few-shot prompting will help the model better understand what it has to do.
But in some circumenstances, you can&rsquo;t find ways around those weird failures,
and the sole solution I found was to make a test <em>retryable</em>.</p>
<p>If a test fails, let&rsquo;s retry a few more times (2 or 3 times) till it passes.
But if it fails everytime in spite of the retries, then it&rsquo;ll just fail as expected.</p>
<p>I wrote JUnit <em>Rules</em> in the past for such situations, but that was in the JUnit 4 days.
Now, I&rsquo;m using JUnit 5, and although it&rsquo;s possible to make JUnit 4 tests run under JUnit 5,
I thought it was a great opportunity to try creating a JUnit 5 <em>extension</em>,
which is the more powerful mechanism that replaces JUnit 4 rules.</p>
<h2 id="it-all-starts-with-a-failing-test-case">It all starts with a failing test case</h2>
<p>Let&rsquo;s say you have an hypothetical flaky test that fails a few times in a row:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>count<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>1;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Test</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">test_custom_junit_retry_extension</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>assertThat(count<span style="color:#666">++</span>).<span style="color:#4070a0">isEqualTo</span>(4);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>The first 3 executions will see an assertion failure, but the 4th would succeed
as the counter is then equal to <code>4</code>.</p>
<p>I&rsquo;d like to annotate this test method with a custom annotation that indicates the number of times I&rsquo;m ready to retry that test:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>count<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>1;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Test</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@ExtendWith</span>(RetryExtension.<span style="color:#4070a0">class</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Retry</span>(4)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">test_custom_junit_retry_extension</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>assertThat(count<span style="color:#666">++</span>).<span style="color:#4070a0">isEqualTo</span>(4);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>This <code>@ExtendWith()</code> annotation indicates that I&rsquo;m registering a JUnit 5 extension.
And <code>@Retry(4)</code> is a custom annotation that I&rsquo;ve created.</p>
<p>Note that <code>@ExtendWith()</code> can be at the class-level, but it can also live at the method level.</p>
<p>Let&rsquo;s have a look at the <code>@Retry</code> annotation:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.lang.annotation.Retention</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.lang.annotation.RetentionPolicy</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#555;font-weight:bold">@Retention</span>(RetentionPolicy.<span style="color:#4070a0">RUNTIME</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#555;font-weight:bold">@interface</span><span style="color:#bbb"> </span>Retry<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#902000">int</span><span style="color:#bbb"> </span><span style="color:#06287e">value</span>()<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">default</span><span style="color:#bbb"> </span>3;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>By default, I attempt the test 3 times, if no number is provided for the annotation value.</p>
<p>Now it&rsquo;s time to see how the extension code works:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">org.junit.jupiter.api.extension.ExtensionContext</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">org.junit.jupiter.api.extension.TestExecutionExceptionHandler</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.concurrent.atomic.AtomicInteger</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">RetryExtension</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">implements</span><span style="color:#bbb"> </span>TestExecutionExceptionHandler<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>AtomicInteger<span style="color:#bbb"> </span>counter<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>AtomicInteger(1);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">printError</span>(Throwable<span style="color:#bbb"> </span>e)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>System.<span style="color:#4070a0">err</span>.<span style="color:#4070a0">println</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;Attempt test execution #&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>counter.<span style="color:#4070a0">get</span>()<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34; failed (&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>e.<span style="color:#4070a0">getClass</span>().<span style="color:#4070a0">getName</span>()<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;thrown):  &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>e.<span style="color:#4070a0">getMessage</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">handleTestExecutionException</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>ExtensionContext<span style="color:#bbb"> </span>extensionContext,<span style="color:#bbb"> </span>Throwable<span style="color:#bbb"> </span>throwable)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">throws</span><span style="color:#bbb"> </span>Throwable<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>printError(throwable);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>extensionContext.<span style="color:#4070a0">getTestMethod</span>().<span style="color:#4070a0">ifPresent</span>(method<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>maxExecutions<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>method.<span style="color:#4070a0">getAnnotation</span>(Retry.<span style="color:#4070a0">class</span>)<span style="color:#bbb"> </span><span style="color:#666">!=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span><span style="color:#666">?</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>method.<span style="color:#4070a0">getAnnotation</span>(Retry.<span style="color:#4070a0">class</span>).<span style="color:#4070a0">value</span>()<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>1;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">while</span><span style="color:#bbb"> </span>(counter.<span style="color:#4070a0">incrementAndGet</span>()<span style="color:#bbb"> </span><span style="color:#666">&lt;=</span><span style="color:#bbb"> </span>maxExecutions)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>extensionContext.<span style="color:#4070a0">getExecutableInvoker</span>().<span style="color:#4070a0">invoke</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span>method,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span>extensionContext.<span style="color:#4070a0">getRequiredTestInstance</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span><span style="color:#007020;font-weight:bold">return</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">catch</span><span style="color:#bbb"> </span>(Throwable<span style="color:#bbb"> </span>t)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>printError(t);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(counter.<span style="color:#4070a0">get</span>()<span style="color:#bbb"> </span><span style="color:#666">&gt;=</span><span style="color:#bbb"> </span>maxExecutions)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#007020;font-weight:bold">throw</span><span style="color:#bbb"> </span>t;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>});<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Let&rsquo;s go through the code step by step:</p>
<ul>
<li>The extension has a counter to count the number of executions</li>
<li>a <code>printError()</code> method is used to report the assertion failure or exception</li>
<li>The class implements the <code>TestExecutionExceptionHandler</code> interface</li>
<li>That interface requires the method <code>handleTestExecutionException()</code> to be implemented</li>
<li>This method is invoked when a test throws some exception</li>
<li>If an exception is thrown, let&rsquo;s see if the method is annotated with the <code>@Retry</code> annotation</li>
<li>and let&rsquo;s retrieve the number of attempts demanded by the developer</li>
<li>Then let&rsquo;s loop to do some more executions of the test method, until it passes or up to the number of attempts</li>
</ul>
<h2 id="missing-standard-junit-5-extension">Missing standard JUnit 5 extension?</h2>
<p>I thought a <code>@Retry</code> extension would be pretty common, and that it would be integrated in JUnit 5 directly.
Or at least, some library would provide common JUnit 5 extensions?
But my search didn&rsquo;t yield anything meaningful.
Did I overlook or miss something?</p>
<p>At least now, I have a solution to work around some flaky tests, thanks to this retryable extension!</p>
<h2 id="going-further">Going further</h2>
<p>If you want to learn more about JUnit 5 extensions,
there were a few resources that helped me develop this extension.
First of all, two artciles from Baeldung on
<a href="https://www.baeldung.com/junit-5-migration">Migrating from JUnit 4 to JUnit 5</a>
to understand the changes since JUnit 4, and this
<a href="https://www.baeldung.com/junit-5-extensions">Guide to JUnit 5 Extensions</a>.
And of course, the JUnit 5 documentation on
<a href="https://junit.org/junit5/docs/current/user-guide/#extensions">extensions</a>.</p>
<blockquote>
<h2 id="update">Update</h2>
<p>I&rsquo;m glad I shared this article on Twitter, because I immediately got a response!
Thanks <a href="https://x.com/donal_tweets">@donal_tweets</a>
for your <a href="https://x.com/donal_tweets/status/1830260408462221622">answer</a>!</p>
<p>The <a href="https://junit-pioneer.org/">JUnit Pioneer</a> library provides a JUnit 5 extension pack,
which includes a powerful <a href="https://junit-pioneer.org/docs/retrying-test/">retrying extension</a>.
Replace the usual <code>@Test</code> annotation with <code>@RetryingTest</code>.
You can specify the number of attempts, the minimum number of successes, or some wait time before retries.</p>
<p>There&rsquo;s also a <a href="https://github.com/artsok/rerunner-jupiter">rerunner</a> extension that is quite similar.</p>
<p>My friend <a href="https://x.com/aheritier">@aheritier</a> also suggested that
Maven Surefire can be configured to automatically
<a href="https://maven.apache.org/surefire/maven-surefire-plugin/examples/rerun-failing-tests.html">retry failing tests</a>
a few times, thanks to a special flag:</p>
<pre tabindex="0"><code>mvn -Dsurefire.rerunFailingTestsCount=2 test
</code></pre><p>In my case, I don&rsquo;t want to retry all failing tests, but only a specific one that I know is flaky.</p>
<p>For those who prefer Gradle over Maven, there&rsquo;s a Gradle plugin as well:
<a href="https://github.com/gradle/test-retry-gradle-plugin">test-retry</a>.
You can configure the behavior in your <code>build.gradle</code> file:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>test <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>   retry <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>       maxRetries <span style="color:#666">=</span> <span style="color:#40a070">2</span>
</span></span><span style="display:flex;"><span>       maxFailures <span style="color:#666">=</span> <span style="color:#40a070">20</span>
</span></span><span style="display:flex;"><span>       failOnPassedAfterRetry <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">true</span>
</span></span><span style="display:flex;"><span>   <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Someone also suggested me to use fuzzy assertions, but my test is very binary as it either fails or succeeds.
There&rsquo;s no threshold, or value that would fit within some bounds.</p></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Let LLM suggest Instagram hashtags for your pictures</title><link>https://glaforge.dev/posts/2024/08/12/let-llm-suggest-instagram-hashtags/</link><pubDate>Mon, 12 Aug 2024 21:15:19 +0200</pubDate><guid>https://glaforge.dev/posts/2024/08/12/let-llm-suggest-instagram-hashtags/</guid><description>&lt;p>In this article, we&amp;rsquo;ll explore another great task where Large Language Models shine: &lt;strong>entity and data extraction&lt;/strong>.
LLMs are really useful beyond just mere chatbots (even smart ones using Retrieval Augmented Generation).&lt;/p>
&lt;p>Let me tell you a little story of a handy application we could build, for wannabe Instagram influencers!&lt;/p>
&lt;h2 id="great-instagram-hashtags-thanks-to-llms">Great Instagram hashtags, thanks to LLMs&lt;/h2>
&lt;p>When posting Instagram pictures, I often struggle with finding the right hashtags to engage with the community.
Large Language Models are pretty creative, and they&amp;rsquo;ve certainly seen a bunch of Instagram pictures with their descriptions.&lt;/p></description><content:encoded>
<![CDATA[<p>In this article, we&rsquo;ll explore another great task where Large Language Models shine: <strong>entity and data extraction</strong>.
LLMs are really useful beyond just mere chatbots (even smart ones using Retrieval Augmented Generation).</p>
<p>Let me tell you a little story of a handy application we could build, for wannabe Instagram influencers!</p>
<h2 id="great-instagram-hashtags-thanks-to-llms">Great Instagram hashtags, thanks to LLMs</h2>
<p>When posting Instagram pictures, I often struggle with finding the right hashtags to engage with the community.
Large Language Models are pretty creative, and they&rsquo;ve certainly seen a bunch of Instagram pictures with their descriptions.</p>
<p>So it&rsquo;s natural to try asking an AI like <a href="https://gemini.google.com/app">Gemini</a> what it could suggest in terms of hashtags:</p>
<p><figure>
  <a href="#img-efd146afabd265680009e826b570b172">
    <img src="/img/gemini/gemini-instagram-hashtags.png"
      alt="Gemini Instagram Hashtag suggestion"
       />
  </a>
  <figcaption>Gemini Instagram Hashtag suggestion</figcaption>
</figure>
<div class="lightbox" id="img-efd146afabd265680009e826b570b172">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/gemini-instagram-hashtags.png"
    alt="Gemini Instagram Hashtag suggestion"
     />
  <div class="lightbox-caption">Gemini Instagram Hashtag suggestion</div>
</div>
</p>
<p>This is a picture taken in the port of Heraklion in Crete, a Greek island.
Here&rsquo;s the <a href="https://g.co/gemini/share/476eb5dd974a">conversation</a> I had with Gemini, if you want to see all the tags it suggested.
I think you&rsquo;ll agree with me that those hashtags look pretty good.
Gemini was able to recognise where the picture was taken, as it had tags like <code>#heraklion</code>, <code>#crete</code>, <code>#greece</code>, <code>#greekisland</code>, etc.
In another attempt, it even told me the name of the fortress of the Venetian port, and suggested other tags along those lines.
We also have several tags typically found on Instagram, like <code>#travelgram</code>, <code>#instatravel</code>, and more specific tags like <code>#cretephotography</code>.</p>
<p>My developer mind started quickly spinning with ideas of an online tool to help users be more creative with their instagram tags.
Armed with my usual tools of trade: Java, and <a href="https://docs.langchain4j.dev/">LangChain4j</a>,
I tried to see how I could implement such a tool.</p>
<p>When you want to integrate an LLM into an application, it&rsquo;s important to be able to use more structured outputs than plain text.
And what&rsquo;s great with the Gemini 1.5 Flash model is that it can generate a JSON response,
and Gemini 1.5 Pro can even follow a specific JSON schema
(also called <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/control-generated-output">controlled generation</a>).</p>
<h2 id="lets-implement-an-instagram-hashtag-generator">Let&rsquo;s implement an Instagram hashtag generator</h2>
<p>First, let&rsquo;s see what the Gemini API responds with a plain prompt, without trying to return some JSON payload:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>modelCreative<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(PROJECT_ID)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>List<span style="color:#666">&lt;</span>ChatMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>messages<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ArrayList<span style="color:#666">&lt;&gt;</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>messages.<span style="color:#4070a0">add</span>(SystemMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    You are an Instagram influencer and expert.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    You master the fine art of choosing the best creative hashtags
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    to share users&#39; best pictures, and to ensure engagement with
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    the Instagram community is the highest possible.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>messages.<span style="color:#4070a0">add</span>(UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>ImageContent.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>Paths.<span style="color:#4070a0">get</span>(<span style="color:#4070a0">&#34;src/main/resources/travel-picture.jpg&#34;</span>).<span style="color:#4070a0">toUri</span>()),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>TextContent.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#4070a0">&#34;What are the best Instagram hashtags to describe that picture?&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>modelCreative.<span style="color:#4070a0">generate</span>(messages);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>responseText<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>response.<span style="color:#4070a0">content</span>().<span style="color:#4070a0">text</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(responseText);<span style="color:#bbb">
</span></span></span></code></pre></div><ul>
<li>First, we instantiate a Vertex AI Gemini chat model</li>
<li>We prepare a list of messages: one system message to describe the task the LLM must achieve, and one compound message made of the picture and the request to generate hashtags for it. Gemini is a <strong>multimodal</strong> model that accepts text, but also images, audios, videos, and PDF files.</li>
<li>Then we ask Gemini to generate a response, which we then print.</li>
</ul>
<p>The Gemini LLM API will reply with a nice and creative response suggesting lots of great hashtags, along with some interesting explanations:</p>
<pre tabindex="0"><code>Here are some relevant hashtags for your picture:

**General Hashtags:**
#greece #greekislands #crete #creteisland #cretelife #holiday #travel
#vacay #instatravel #travelgram #beautifuldestinations
#travelphotography #photooftheday #instagood #igdaily #instapic

**Specific Hashtags:**
#heraklion #heraklioncrete #venetianharbour #harbourlife #boatlife
#boatday #greekboats #greekharbour #beautifulcrete #creteharbour
#cretevibes #greece_gram #crete_gram #greece_travel

**Trending Hashtags:**
#wanderlust #traveltuesday #instagood #travelgram
#beautifuldestinations #adventuretime

**Hashtags for Engagement:**
#explorecrete #cretelove #lovegreece #greecevibes #cretephoto
#creteexperience #discovercrete

Make sure to also use a mix of hashtags with different lengths and
popularity. You can also use location hashtags to target people in
your area.

For example, you could use hashtags like #heraklioncrete or
#creteharbour to target people who are interested in visiting that
specific location.

You should also consider using a mix of popular and less popular
hashtags. Popular hashtags will help your post reach a wider audience,
while less popular hashtags can help your post stand out from the
crowd.

**Tips for Choosing Hashtags:**

* Research relevant hashtags. Look at what other users are using and
see what&#39;s trending.
* Use a mix of general and specific hashtags.
* Don&#39;t use too many hashtags.
* Keep your hashtags relevant to your image.

By using a mix of relevant and engaging hashtags, you can increase
your chances of getting your post seen by a wider audience and improve
your engagement on Instagram.
</code></pre><p>Now, let&rsquo;s tweak this code to output the Instagram hashtags as a JSON array of hashtag strings.
With LangChain4j&rsquo;s Gemini integration, it&rsquo;s pretty trivial, we can specify that we want to return JSON payloads,
thanks to the <code>responseMimeType()</code> method:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>modelCreative<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(PROJECT_ID)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseMimeType</span>(<span style="color:#4070a0">&#34;application/json&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Let&rsquo;s update the system instruction to be explicit about what the JSON should look like:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>List<span style="color:#666">&lt;</span>ChatMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>messages<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ArrayList<span style="color:#666">&lt;&gt;</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>messages.<span style="color:#4070a0">add</span>(SystemMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    You are an Instagram influencer and expert.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    You master the fine art of choosing the best creative hashtags
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    to share users&#39; best pictures, and to ensure engagement with
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    the Instagram community is the highest possible.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Return a JSON array containing the hashtags as strings, for example:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    ```json
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    [&#34;#beach&#34;, &#34;#island&#34;, &#34;#traveltahiti&#34;]
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    ```
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>));<span style="color:#bbb">
</span></span></span></code></pre></div><p>Now let&rsquo;s see the LLM&rsquo;s response:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>[
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#greece&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#crete&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#heraklion&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#cretelife&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#mediterraneansea&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#creteisland&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#greecevacations&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#greekislands&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#cretetravel&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#heraklionport&#34;</span>
</span></span><span style="display:flex;"><span>]
</span></span></code></pre></div><p>On one hand, it&rsquo;s nice that Gemini obeyed us and generated the request JSON array of hashtags.
However, notice that there are fewer hashtags, which are also a bit less creative.</p>
<h2 id="llms-are-less-creative-when-constrained">LLMs are less creative when constrained</h2>
<p>It&rsquo;s not the first time I noticed that behavior with an LLM.
I tried this with other LLMs like ChatGPT.
It seems that LLMs are less creative when they are constrained to follow a stricter output.</p>
<p>And indeed, what actually trigged this article idea and example was this paper that I came across:
<a href="https://arxiv.org/abs/2408.02442#">Let Me Speak Freely? A Study on the Impact of
Format Restrictions on Performance of Large Language Models</a>
which has been published on arXiv a few days ago,
which confirms my intuition that LLMs are less creative when using controlled generation:</p>
<blockquote>
<p>Structured generation, the process of producing content in standardized formats like JSON and XML,
is widely utilized in real-world applications to extract key output information from large language models (LLMs).
This study investigates whether such constraints on generation space impact LLMs&rsquo; abilities,
including reasoning and domain knowledge comprehension.
Specifically, we evaluate LLMs&rsquo; performance when restricted to adhere to structured formats
versus generating free-form responses across various common tasks.
Surprisingly, <strong>we observe a significant decline in LLMs&rsquo; reasoning abilities under format restrictions</strong>.
Furthermore, we find that stricter format constraints generally lead to greater performance degradation in reasoning tasks.</p></blockquote>
<h2 id="a-better-solution-with-a-two-step-approach-with-entity-extraction">A better solution with a two-step approach with entity extraction</h2>
<p>Since LLMs are not as good when we control their generation, we can try a slighly smarter approach:</p>
<ul>
<li>Firstly, we can ask the LLM to give its usual plain-text creative answer,</li>
<li>Secondly, we ask the LLM to actually extract all the hashtags from the previous response, using controlled gneration.</li>
</ul>
<p>LLMs are great at various classical Natural Language Processing tasks like <strong>entity extraction</strong>.
And here, indeed, what we want is to just extract the hashtags from the plain-text response.</p>
<p>For such a task, controlled generation won&rsquo;t hinder the creativity, and will be acurate and extract correctly all the tags.
The aforementioned paper seemed to also hint at the fact that controlled generation can actually help with some tasks like classification.</p>
<p>Let&rsquo;s have a look at our improved approach.
We keep the first attempt from the beginning of this article, without using controlled generation,
but we&rsquo;ll use a different configuration for the second step:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>modelExtraction<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(PROJECT_ID)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-pro&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseSchema</span>(SchemaHelper.<span style="color:#4070a0">fromClass</span>(String<span style="color:#666">[]</span>.<span style="color:#4070a0">class</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>List<span style="color:#666">&lt;</span>ChatMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>messagesForExtraction<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ArrayList<span style="color:#666">&lt;&gt;</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>messagesForExtraction.<span style="color:#4070a0">add</span>(SystemMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Your job is to extract Instagram hashtags from a given text, and
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    return them as a JSON array of strings representing those hashtags.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>messagesForExtraction.<span style="color:#4070a0">add</span>(UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Here is the text to extract Instagram hashtags from:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>responseText));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>responseFromExtraction<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>modelExtraction.<span style="color:#4070a0">generate</span>(messagesForExtraction);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>extractedTagsJson<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>responseFromExtraction.<span style="color:#4070a0">content</span>().<span style="color:#4070a0">text</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(extractedTagsJson);<span style="color:#bbb">
</span></span></span></code></pre></div><ul>
<li>In this example, I used Gemini 1.5 Pro instead of Gemini 1.5 Flash to show you the use of the
<code>responseSchema()</code> method which allows you to specify the exact shape of the JSON we want to retrieve.
I could have used Gemini 1.5 Flash like before, but I have to give a bit more prompting help to specify the JSON schema.</li>
<li>This time, we use a different system message to explain the task of hashtag extraction.</li>
<li>And the user message reuses the creative response from the previous LLM call to extract hashtags from it.</li>
</ul>
<p>So what&rsquo;s the output like?</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>[
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#greece&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#greekislands&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#crete&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#creteisland&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#cretelife&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#holiday&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#travel&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#vacay&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#instatravel&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#travelgram&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#beautifuldestinations&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#travelphotography&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#photooftheday&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#instagood&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#igdaily&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#instapic&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#heraklion&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#heraklioncrete&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#venetianharbour&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#harbourlife&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#boatlife&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#boatday&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#greekboats&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#greekharbour&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#beautifulcrete&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#creteharbour&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#cretevibes&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#greece_gram&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#crete_gram&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#greece_travel&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#wanderlust&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#traveltuesday&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#instagood&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#travelgram&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#beautifuldestinations&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#adventuretime&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#explorecrete&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#cretelove&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#lovegreece&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#greecevibes&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#cretephoto&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#creteexperience&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;#discovercrete&#34;</span>
</span></span><span style="display:flex;"><span>]
</span></span></code></pre></div><p>Excellent! It managed to extract all the tags of the creative response!</p>
<h2 id="conclusion--discussion">Conclusion &amp; discussion</h2>
<p>Even if researchers found that LLMs may be less creative when constrained with controlled generation,
we can find workarounds to prevent suffering from this limitation, like we did with this two-step approach
by making two calls.
The first call is a creative one, while the second is the data extraction one.</p>
<p>One drawback of this approach, however, is that we had to make two calls to the LLM.
So this can be a bit more costly in terms of tokens generated.
And it also adds latency, because we have two calls instead of just one.
So you might have to balance cost &amp; lantency with quality, depending on your use case.</p>
<p>But it&rsquo;s always great to have the choice!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Gemini Nano running locally in your browser</title><link>https://glaforge.dev/posts/2024/08/07/gemini-nano-running-locally-in-your-browser/</link><pubDate>Wed, 07 Aug 2024 15:57:33 +0200</pubDate><guid>https://glaforge.dev/posts/2024/08/07/gemini-nano-running-locally-in-your-browser/</guid><description>&lt;p>Generative AI use cases are usually about running large language models somewhere in the cloud.
However, with the advent of smaller models and open models, you can run them locally on your machine,
with projects like &lt;a href="https://github.com/ggerganov/llama.cpp">llama.cpp&lt;/a> or &lt;a href="https://ollama.com/">Ollama&lt;/a>.&lt;/p>
&lt;p>And what about in the browser?
With &lt;a href="https://github.com/google-ai-edge/mediapipe">MediaPipe&lt;/a> and &lt;a href="https://www.tensorflow.org/js">TensorFlow.js&lt;/a>,
you can train and run small neural networks for tons of fun and useful tasks
(like recognising hand movements through the webcam of your computer), and it&amp;rsquo;s also possible to run
&lt;a href="https://ai.google.dev/gemma/">Gemma&lt;/a> 2B and even 7B models.&lt;/p></description><content:encoded>
<![CDATA[<p>Generative AI use cases are usually about running large language models somewhere in the cloud.
However, with the advent of smaller models and open models, you can run them locally on your machine,
with projects like <a href="https://github.com/ggerganov/llama.cpp">llama.cpp</a> or <a href="https://ollama.com/">Ollama</a>.</p>
<p>And what about in the browser?
With <a href="https://github.com/google-ai-edge/mediapipe">MediaPipe</a> and <a href="https://www.tensorflow.org/js">TensorFlow.js</a>,
you can train and run small neural networks for tons of fun and useful tasks
(like recognising hand movements through the webcam of your computer), and it&rsquo;s also possible to run
<a href="https://ai.google.dev/gemma/">Gemma</a> 2B and even 7B models.</p>
<p>But there&rsquo;s something interesting cooking these days: <strong>built-in language models in the browser</strong>!</p>
<p>The Chrome developers are working on a new Web API to integrate LLMs in the browser,
and are experimenting with the <a href="https://deepmind.google/technologies/gemini/nano/">Gemini Nano</a> model
(already integrated in some smartphones like Samsung Galaxy or Google Pixel phones)
inside <a href="https://www.google.com/chrome/canary/">Chrome Canary</a>.</p>
<h2 id="getting-started-with-gemini-nano-and-chrome-canary">Getting started with Gemini Nano and Chrome Canary</h2>
<p>I&rsquo;m sure you want to experiment with that too? Let&rsquo;s see how to proceed:</p>
<ul>
<li>
<p>First of all, you&rsquo;ll need to download <a href="https://www.google.com/chrome/canary/">Chrome Canary</a></p>
</li>
<li>
<p>In <code>chrome://flags</code>, you must <strong>enable</strong> two experiments:</p>
<ul>
<li><code>Prompt API for Gemini Nano</code> and</li>
<li><code>Enables optimization guide on device</code>.</li>
</ul>
</li>
<li>
<p>You&rsquo;ll have to restart the browser, after having enabled those two flags.</p>
</li>
</ul>
<p>It may take quite a bit of time to download Gemini Nano (as it&rsquo;s a small model, it takes only around 1.7GB of space, but you&rsquo;ll need about 20GB at installation time on your hard drive)
but the API will tell you if the model weights are not fully downloaded yet.</p>
<h2 id="experimenting-in-the-playground">Experimenting in the playground</h2>
<p>Now it&rsquo;s time to play!
Let&rsquo;s see what this embedded Gemini Nano can do, in the <a href="https://chrome.dev/prompt-api-playground/">Prompt API playground</a>.
This is a simple form where you can send prompts to the model, and see its replies.</p>
<p><figure>
  <a href="#img-9cfc8d49ee281ab5fc1dabefa1e91e8e">
    <img src="/img/gemini/nano-playground.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-9cfc8d49ee281ab5fc1dabefa1e91e8e">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/nano-playground.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Looks like it&rsquo;s smart enough to know that no cat ever went on the moon!</p>
<h2 id="a-bit-of-code">A bit of code</h2>
<p>The <a href="https://github.com/tomayac/prompt-api-playground">code of this demo</a> is available on Github.</p>
<p>Let&rsquo;s have a look at the key lines of the <strong>Prompt API</strong> usage.</p>
<p>To know if the browser supports the Prompt API, you&rsquo;ll need to check the existence of the new <code>ai</code> object on <code>window</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">if</span> (<span style="color:#666">!</span><span style="color:#007020">window</span>.ai) {
</span></span><span style="display:flex;"><span>    ...
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Then you&rsquo;ll have to create a <strong>text session</strong> with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> session <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">await</span> <span style="color:#007020">window</span>.ai.createTextSession();
</span></span></code></pre></div><p>Then you can either wait for the full response, or stream the tokens as they are generated.
Here, let&rsquo;s see the streaming scenario, and how to iterate over the streamed tokens:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> stream <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">await</span> session.promptStreaming(
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;What&#39;s the name of the first cat who stepped on the moon?&#34;</span>
</span></span><span style="display:flex;"><span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">for</span> <span style="color:#007020;font-weight:bold">await</span> (<span style="color:#007020;font-weight:bold">const</span> chunk <span style="color:#007020;font-weight:bold">of</span> stream) {
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">var</span> fullResponse <span style="color:#666">=</span> chunk.trim();
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// do something with the response, like appending it to a DOM node
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>}
</span></span></code></pre></div><p>If you&rsquo;re not streaming the response, you can also do as follows,
to get the response in one go once it&rsquo;s fully generated:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> result <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">await</span> session.prompt(
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;What&#39;s the name of the first cat who stepped on the moon?&#34;</span>
</span></span><span style="display:flex;"><span>);
</span></span></code></pre></div><h2 id="but-why-running-ai-in-the-browser">But why running AI in the browser?</h2>
<p>Maybe I should have started there, afterall?
Why would you want to run models locally in the browser, rather than using a cloud-hosted one?</p>
<p>As the <a href="https://developer.chrome.com/docs/ai/built-in">documentation</a> outlines:</p>
<ul>
<li>For <strong>privacy reasons</strong>: you may want to do local processing of sensitive data, to avoid sending such information on the web.</li>
<li>For <strong>latency gains</strong>: once the model is loaded in the browser (in about 3 seconds on my machine), the model responds super fast to all subsequent requests.
So you can have a very snappy experience, without the long roundtrip through the internet!</li>
<li>For <strong>lower costs</strong>: since all the AI inference is done in the browser, it&rsquo;s not going to cost you anything on the server-side.</li>
<li>For <strong>offline usage</strong>: as it runs in the browser, even if you lost your internet connection, your Web UI will continue to function with all its smart AI features.</li>
</ul>
<h2 id="interesting-resources">Interesting resources</h2>
<ul>
<li>Checkout the <a href="https://chrome.dev/prompt-api-playground/">Prompt API playground</a> to play with it (after having followed the instructions above).</li>
<li>Have a look at the <a href="https://github.com/tomayac/prompt-api-playground">sources</a> of the playground to learn how the demo is done.</li>
<li>There&rsquo;s a nice <a href="https://medium.com/google-cloud/google-chrome-has-a-secret-ai-assistant-9accb95f1911">publication</a> that shows how to use the Prompt API to summarize the content of the web page displayed in your browser.</li>
<li>The HuggingFace people have an <a href="https://huggingface.co/blog/Xenova/run-gemini-nano-in-your-browser">extended article</a> on how to run Gemini Nano in the browser, with some advanced details about the Prompt API.</li>
<li>Read the pages that explains the <a href="https://developer.chrome.com/docs/ai/built-in">goals of the built-in AI</a>.</li>
<li>It&rsquo;s interesting to glance through the <a href="https://github.com/explainers-by-googlers/prompt-api/">explainer</a> of the Prompt API to understand how it&rsquo;s been designed.</li>
<li>And the best resource for the end, the <a href="https://docs.google.com/document/d/1VG8HIyz361zGduWgNG7R_R8Xkv0OOJ8b5C9QKeCjU0c/edit">user guide of the built-in AI early preview</a>, which gives lots of details about the Prompt API.</li>
</ul>
<h2 id="summary">Summary</h2>
<p>I&rsquo;ve been focusing mostly on large language models in the cloud so far,
in particular <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/start/quickstarts/quickstart-multimodal#gemini-text-only-samples-java">Gemini</a>,
but I&rsquo;m excited at the prospect of the interesting use cases that it can enable.</p>
<p>Imagine, for example, a travel itinerary application, that would store all the information of your trip locally (in IndexedDB or a WebAssembly-fied sqlite),
and you could ask offline all the questions you want about the journey? (basically, <strong>RAG in the browser</strong>!)
No need to hunt for a public wifi network or a local SIM card.</p>
<p>There are also many tasks some browser extension could handle:</p>
<ul>
<li>When preparing my podcast episode and show notes, I could ask Gemini Nano to make a 5-bullet-point summary of the article I&rsquo;m reading.</li>
<li>When reading the reviews for a product, I could get a sentiment analysis signal that tells me if customers are happy with that product.</li>
</ul>
<p>We could also think of some hybrid scenarios, as both cloud-hosted and local-running models could complement each other.</p>
<p>I hope this Web API will become a standard and that other browsers support it too, and offer different models as well.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Sentiment analysis with few-shot prompting</title><link>https://glaforge.dev/posts/2024/07/30/sentiment-analysis-with-few-shots-prompting/</link><pubDate>Tue, 30 Jul 2024 13:06:16 +0200</pubDate><guid>https://glaforge.dev/posts/2024/07/30/sentiment-analysis-with-few-shots-prompting/</guid><description>&lt;p>In a rencent article, we talked about
&lt;a href="https://glaforge.dev/posts/2024/07/11/text-classification-with-gemini-and-langchain4j/">text classification&lt;/a>
using &lt;a href="https://deepmind.google/technologies/gemini/">Gemini&lt;/a> and &lt;a href="https://docs.langchain4j.dev/">LangChain4j&lt;/a>.&lt;/p>
&lt;p>A typical example of text classification is the case of &lt;strong>sentiment analysis&lt;/strong>.&lt;/p>
&lt;p>In my LangChain4j-powered Gemini &lt;a href="https://github.com/glaforge/gemini-workshop-for-java-developers/">workshop&lt;/a>,
I used this use case to illustrate the classification problem:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>ChatLanguageModel&lt;span style="color:#bbb"> &lt;/span>model&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#bbb"> &lt;/span>VertexAiGeminiChatModel.&lt;span style="color:#4070a0">builder&lt;/span>()&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>.&lt;span style="color:#4070a0">project&lt;/span>(System.&lt;span style="color:#4070a0">getenv&lt;/span>(&lt;span style="color:#4070a0">&amp;#34;PROJECT_ID&amp;#34;&lt;/span>))&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>.&lt;span style="color:#4070a0">location&lt;/span>(System.&lt;span style="color:#4070a0">getenv&lt;/span>(&lt;span style="color:#4070a0">&amp;#34;LOCATION&amp;#34;&lt;/span>))&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>.&lt;span style="color:#4070a0">modelName&lt;/span>(&lt;span style="color:#4070a0">&amp;#34;gemini-1.5-flash-001&amp;#34;&lt;/span>)&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>.&lt;span style="color:#4070a0">maxOutputTokens&lt;/span>(10)&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>.&lt;span style="color:#4070a0">maxRetries&lt;/span>(3)&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>.&lt;span style="color:#4070a0">build&lt;/span>();&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>PromptTemplate&lt;span style="color:#bbb"> &lt;/span>promptTemplate&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#bbb"> &lt;/span>PromptTemplate.&lt;span style="color:#4070a0">from&lt;/span>(&lt;span style="color:#4070a0">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> Analyze the sentiment of the text below.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> Respond only with one word to describe the sentiment.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> INPUT: This is fantastic news!
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> OUTPUT: POSITIVE
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> INPUT: Pi is roughly equal to 3.14
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> OUTPUT: NEUTRAL
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> INPUT: I really disliked the pizza. Who would use pineapples as a pizza topping?
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> OUTPUT: NEGATIVE
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> INPUT: {{text}}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> OUTPUT:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>);&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>Prompt&lt;span style="color:#bbb"> &lt;/span>prompt&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#bbb"> &lt;/span>promptTemplate.&lt;span style="color:#4070a0">apply&lt;/span>(&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>Map.&lt;span style="color:#4070a0">of&lt;/span>(&lt;span style="color:#4070a0">&amp;#34;text&amp;#34;&lt;/span>,&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;I love strawberries!&amp;#34;&lt;/span>));&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>Response&lt;span style="color:#666">&amp;lt;&lt;/span>AiMessage&lt;span style="color:#666">&amp;gt;&lt;/span>&lt;span style="color:#bbb"> &lt;/span>response&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#bbb"> &lt;/span>model.&lt;span style="color:#4070a0">generate&lt;/span>(prompt.&lt;span style="color:#4070a0">toUserMessage&lt;/span>());&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>System.&lt;span style="color:#4070a0">out&lt;/span>.&lt;span style="color:#4070a0">println&lt;/span>(response.&lt;span style="color:#4070a0">content&lt;/span>().&lt;span style="color:#4070a0">text&lt;/span>());&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I used a &lt;code>PromptTemplate&lt;/code> to craft the prompt, with a &lt;code>{{text}}&lt;/code> placeholder value to analyze the sentiment of that particular text.&lt;/p></description><content:encoded>
<![CDATA[<p>In a rencent article, we talked about
<a href="https://glaforge.dev/posts/2024/07/11/text-classification-with-gemini-and-langchain4j/">text classification</a>
using <a href="https://deepmind.google/technologies/gemini/">Gemini</a> and <a href="https://docs.langchain4j.dev/">LangChain4j</a>.</p>
<p>A typical example of text classification is the case of <strong>sentiment analysis</strong>.</p>
<p>In my LangChain4j-powered Gemini <a href="https://github.com/glaforge/gemini-workshop-for-java-developers/">workshop</a>,
I used this use case to illustrate the classification problem:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ChatLanguageModel<span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash-001&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">maxOutputTokens</span>(10)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">maxRetries</span>(3)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>PromptTemplate<span style="color:#bbb"> </span>promptTemplate<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>PromptTemplate.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Analyze the sentiment of the text below.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Respond only with one word to describe the sentiment.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    INPUT: This is fantastic news!
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    OUTPUT: POSITIVE
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    INPUT: Pi is roughly equal to 3.14
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    OUTPUT: NEUTRAL
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    INPUT: I really disliked the pizza. Who would use pineapples as a pizza topping?
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    OUTPUT: NEGATIVE
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    INPUT: {{text}}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    OUTPUT:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Prompt<span style="color:#bbb"> </span>prompt<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>promptTemplate.<span style="color:#4070a0">apply</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;text&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;I love strawberries!&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(prompt.<span style="color:#4070a0">toUserMessage</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response.<span style="color:#4070a0">content</span>().<span style="color:#4070a0">text</span>());<span style="color:#bbb">
</span></span></span></code></pre></div><p>I used a <code>PromptTemplate</code> to craft the prompt, with a <code>{{text}}</code> placeholder value to analyze the sentiment of that particular text.</p>
<p>Notice that I used the <a href="https://learnprompting.org/docs/basics/few_shot">few-shot prompting</a> technique, with example inputs and outputs.</p>
<h2 id="few-shot-prompting-with-a-list-of-messages">Few-shot prompting with a list of messages</h2>
<p>Somehow, I had the impression that this <code>INPUT/OUTPUT</code> notation was a bit of a <em>hack</em>
to encourage the LLM to believe this is an actual exchange between the user and the AI.</p>
<p>I believed it would be cleaner to use a real list of messages that alternate user and AI messages.
So I implemented this alternative approach, but haven&rsquo;t yet committed it to my workshop repository.</p>
<p>Meanwhile, as I was chatting with my colleague <a href="https://x.com/ddobrin">Dan Dobrin</a>,
he pointed me at this very recent blog <a href="https://blog.langchain.dev/few-shot-prompting-to-improve-tool-calling-performance/">post</a>
from the LangChain people, who were investigating <em>few-shot prompting to improve tool-calling performance</em>.</p>
<p>What&rsquo;s interesting in their analysis was that overall, on this anecdata example,
it seems <strong>LLMs do better with real user/AI messages than with a big string of inputs/outputs</strong>.</p>
<p>Let&rsquo;s see how to implement the same approach, with a real exchange of messages:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>List<span style="color:#666">&lt;</span>ChatMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>fewShotPrompts<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>List.<span style="color:#4070a0">of</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>SystemMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Analyze the sentiment of the text below.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Respond only with one word to describe the sentiment.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;This is fantastic news!&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>AiMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;POSITIVE&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;Pi is roughly equal to 3.14&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>AiMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;NEUTRAL&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;I really disliked the pizza. &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                     </span><span style="color:#4070a0">&#34;Who would use pineapples as a pizza topping?&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>AiMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;NEGATIVE&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;I love strawberries!&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(fewShotPrompts);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response.<span style="color:#4070a0">content</span>().<span style="color:#4070a0">text</span>());<span style="color:#bbb">
</span></span></span></code></pre></div><p>This is not much more verbose than the previous approach, as it&rsquo;s still very readable.
And when pulling the few-shot data from an external database, it feels cleaner than concatenating a big string.</p>
<h2 id="more-type-safe-few-shot-prompting-with-messages-and-aiservices">More type-safe few-shot prompting with messages and AiServices</h2>
<p>To further improve on the list of messages tactic, we can use LangChain4j&rsquo;s <code>AiServices</code> concept,
which is a higher-level abstraction than using the model and prompt templates directly.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">enum</span><span style="color:#bbb"> </span>Sentiment<span style="color:#bbb"> </span>{<span style="color:#bbb"> </span>POSITIVE,<span style="color:#bbb"> </span>NEUTRAL,<span style="color:#bbb"> </span>NEGATIVE<span style="color:#bbb"> </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">interface</span> <span style="color:#0e84b5;font-weight:bold">SentimentAnalysis</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@SystemMessage</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Analyze the sentiment of the text below.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Respond only with one word to describe the sentiment.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Sentiment<span style="color:#bbb"> </span><span style="color:#06287e">analyze</span>(String<span style="color:#bbb"> </span>text);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>MessageWindowChatMemory<span style="color:#bbb"> </span>memory<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>MessageWindowChatMemory.<span style="color:#4070a0">withMaxMessages</span>(10);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>memory.<span style="color:#4070a0">add</span>(UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;This is fantastic news!&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>memory.<span style="color:#4070a0">add</span>(AiMessage.<span style="color:#4070a0">from</span>(Sentiment.<span style="color:#4070a0">POSITIVE</span>.<span style="color:#4070a0">name</span>()));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>memory.<span style="color:#4070a0">add</span>(UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;Pi is roughly equal to 3.14&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>memory.<span style="color:#4070a0">add</span>(AiMessage.<span style="color:#4070a0">from</span>(Sentiment.<span style="color:#4070a0">NEUTRAL</span>.<span style="color:#4070a0">name</span>()));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>memory.<span style="color:#4070a0">add</span>(UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;I really disliked the pizza. &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Who would use pineapples as a pizza topping?&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>memory.<span style="color:#4070a0">add</span>(AiMessage.<span style="color:#4070a0">from</span>(Sentiment.<span style="color:#4070a0">NEGATIVE</span>.<span style="color:#4070a0">name</span>()));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>SentimentAnalysis<span style="color:#bbb"> </span>analyzer<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>AiServices.<span style="color:#4070a0">builder</span>(SentimentAnalysis.<span style="color:#4070a0">class</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">chatLanguageModel</span>(model)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">chatMemory</span>(memory)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(analyzer.<span style="color:#4070a0">analyze</span>(<span style="color:#4070a0">&#34;I love strawberries!&#34;</span>));<span style="color:#bbb">
</span></span></span></code></pre></div><p>This third and final approach may be a bit more verbose, and introduces a few more LangChain4j concepts
like system messages, chat memory, and the AI service itself, but it has the advantages of being:</p>
<ul>
<li><strong>more type-safe</strong>, as we&rsquo;re using a <code>Sentiment</code> enum, which is easier to manipulate from code,</li>
<li>cleaner, because we&rsquo;re <strong>using system instructions</strong> to instruct the model about what its job is.</li>
</ul>
<p>We created:</p>
<ul>
<li>a Java <code>enum</code> to represent the possible values of the sentiment,</li>
<li>a <code>SentimentAnalysis</code> interface with a clear signature: a text in input, a <code>Sentiment</code> enum value in output,</li>
<li>a <code>@SystemMessage</code> instruction to describe the analysis task,</li>
<li>a <code>ChatMemory</code> (here a <code>MessageWindowChatMemory</code>) to hold the few-shot examples.</li>
</ul>
<p>Then we bind everything together, thanks to <code>AiServices</code>:
the analysis interface that LangChain4j will implement for us, the language model, and the chat memory.</p>
<p>Finally, users just have to call the <code>analyze()</code> method, passing the text to analyze.</p>
<p>I also like the fact that we are <strong>coding against an interface</strong>, and potentially later on,
developers <strong>could swap the implementation</strong> of the sentiment analyzer, and use a different approach.</p>
<h2 id="conclusion">Conclusion</h2>
<p>All three approaches are valid: a big string, a low-level list of messages, or an <code>AiServices</code> abstraction.
But I have a slight preference for the approach that is more type-safe and less <em>stringy</em>.</p>
<p>Just like LangChain4j provides a <code>TextClassification</code> class that leverages vector embeddings for text similarity,
we could investigate whether it would make sense to also add a few-shot prompting classificaction solution directly in the LangChain4j project.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Analyzing video, audio and PDF files with Gemini and LangChain4j</title><link>https://glaforge.dev/posts/2024/07/25/analyzing-videos-audios-and-pdfs-with-gemini-in-langchain4j/</link><pubDate>Thu, 25 Jul 2024 20:08:52 +0200</pubDate><guid>https://glaforge.dev/posts/2024/07/25/analyzing-videos-audios-and-pdfs-with-gemini-in-langchain4j/</guid><description>&lt;p>Certain models like Gemini are &lt;strong>multimodal&lt;/strong>.
This means that they accept more than just text as input.
Some models support text and images, but &lt;strong>Gemini goes further and also supports audio, video, and PDF files&lt;/strong>.
So you can mix and match text prompts and different multimedia files or PDF documents.&lt;/p>
&lt;p>Until LangChain4j 0.32, the models could only support text and images,
but since my &lt;a href="https://github.com/langchain4j/langchain4j/pull/1464">PR&lt;/a> got merged into the newly released
&lt;a href="https://github.com/langchain4j/langchain4j/releases/tag/0.33.0">0.33&lt;/a> version,
you can use all those files with the LangChain4j Gemini module!&lt;/p></description><content:encoded>
<![CDATA[<p>Certain models like Gemini are <strong>multimodal</strong>.
This means that they accept more than just text as input.
Some models support text and images, but <strong>Gemini goes further and also supports audio, video, and PDF files</strong>.
So you can mix and match text prompts and different multimedia files or PDF documents.</p>
<p>Until LangChain4j 0.32, the models could only support text and images,
but since my <a href="https://github.com/langchain4j/langchain4j/pull/1464">PR</a> got merged into the newly released
<a href="https://github.com/langchain4j/langchain4j/releases/tag/0.33.0">0.33</a> version,
you can use all those files with the LangChain4j Gemini module!</p>
<p>Let&rsquo;s have a look!</p>
<h2 id="getting-the-transcription-of-a-podcast-recording">Getting the transcription of a podcast recording</h2>
<p>Are you an avid podcast listener and want to read its transcription?
Or you want to publish that transcription as show-notes of your own podcast on your website?</p>
<p>You can ask Gemini for the transcription with the following code:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(PROJECT_ID)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-pro&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>AudioContent.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;https://storage.googleapis.com/cloud-samples-data/&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;generative-ai/audio/pixel.mp3&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>TextContent.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;Write a transcription of this audio file&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response.<span style="color:#4070a0">content</span>().<span style="color:#4070a0">text</span>());<span style="color:#bbb">
</span></span></span></code></pre></div><p>Above, we created an audio content object with the <code>AudioContent.from(...)</code> method.
This method can take a string which can be a direct URL to a file on the web,
it can be a Google Cloud Storage URL as well (like <code>gs://bucket/audio.mp3</code>).
It is possible to load a local file from your file system with <code>AudioContent.from(Paths.get(&quot;audio.mp3&quot;).toUri())</code>.
You can even pass the base 64 encoded content of the audio file and specify its mime type.</p>
<h3 id="what-else-could-you-do-with-audio-files">What else could you do with audio files?</h3>
<ul>
<li>
<p>If you&rsquo;re in a hurry and don&rsquo;t have time to listen to this one-hour episode,
instead of asking for the whole transcript, you could change the prompt to ask for a summary.
That way you know if it&rsquo;s worth spending an hour to listen to it all.</p>
</li>
<li>
<p>Gemini also accepts several audio files in input, so if you are recording interviews of persons on a specific topic,
you could ask Gemini to contrast the differences in those responses.</p>
</li>
</ul>
<h2 id="preparing-youtube-video-chaptering">Preparing YouTube video chaptering</h2>
<p>Let&rsquo;s say you&rsquo;re a YouTuber, and you want to do your own video chaptering, instead of relying on the the automatic chapters.
How can you do that?</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(PROJECT_ID)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>VideoContent.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;https://storage.googleapis.com/cloud-samples-data/&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;generative-ai/video/behind_the_scenes_pixel.mp4&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>TextContent.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;Prepare chapters for this video file, &#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;using the YouTube chapter notation&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response.<span style="color:#4070a0">content</span>().<span style="color:#4070a0">text</span>());<span style="color:#bbb">
</span></span></span></code></pre></div><p>For this video, the chapters generated look as follows:</p>
<pre tabindex="0"><code>00:00 Making a Film with a Blind Director
00:16 Adam Morse, Filmmaker
00:28 The Film Shoot
00:48 A Blind Man &amp; His Girlfriend
01:15 Google Pixel Phone
01:33 Guided Frame
02:06 The Technical Crew
02:32 Visual Effects
02:45 Misconceptions About Blindness
03:20 Filmmaking with a Team
03:46 Google Accessibility
04:00 One Person&#39;s Perspective
04:29 Adam&#39;s Vision
05:03 A Beautiful Position
05:19 Google Logo
</code></pre><h3 id="what-else-could-you-do-with-videos">What else could you do with videos?</h3>
<ul>
<li>
<p>If a video of your meeting or your conference presentation has been recorded,
you could use this approach to ask Gemini for a summary of the video, to get the various sections, to write the transcript.</p>
</li>
<li>
<p>We often record videos of our family, our children, etc.
It&rsquo;s not always easy to <em>search</em> through those videos.
You could ask Gemini to provide a summary of the video,
that you would then index with some search engine, or just do some simple <em>grep</em> search from the command-line.</p>
</li>
</ul>
<h2 id="asking-questions-about-pdf-documents">Asking questions about PDF documents</h2>
<p>Let&rsquo;s have a look at one last example: PDF documents.</p>
<p>With LangChain4j, it&rsquo;s possible to use the Apache Tika-based document loader to get the text content of a PDF.
However, you loose some important semantic information, as the layout may be important,
or the figures may convey as well some critical details.</p>
<p>Fortunately, Gemini can ingest PDF documents directly, without an intermediate text transcription.</p>
<p>This allows you to ask questions about PDf documents, and since Gemini has a very large context window,
it&rsquo;s able to analyze very big documents, or several documents at the same time,
without having to implement your own RAG system (Retrieval Augmented Generation).</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(PROJECT_ID)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>PdfFileContent.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;https://proceedings.neurips.cc/paper_files/paper/2017&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#666">+</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;/file/3f5ee243547dee91fbd053c1c4a845aa-Paper.pdf&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>TextContent.<span style="color:#4070a0">from</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;Give a summary of this paper&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response.<span style="color:#4070a0">content</span>().<span style="color:#4070a0">text</span>());<span style="color:#bbb">
</span></span></span></code></pre></div><p>This example analyzes the famous <em>&ldquo;Attention is all you need&rdquo;</em> paper that introduced the concept of <em>Transformer</em> neural networks:</p>
<pre tabindex="0"><code>This paper proposes a novel neural network architecture called the
Transformer, which relies entirely on an attention mechanism and
dispenses with recurrence and convolutions. The Transformer
outperforms existing models on two machine translation tasks, WMT
2014 English-to-German and WMT 2014 English-to-French, while
requiring significantly less training time. The authors argue that
the Transformer&#39;s ability to learn global dependencies without
regard to their distance in the input or output sequences, as well
as its parallelizable nature, make it a promising approach for
sequence modeling and transduction problems. They also present an
analysis of the Transformer&#39;s different components and their effect
on performance. The paper concludes by discussing potential future
directions for research.
</code></pre><h3 id="what-else-could-you-do-with-pdf-documents">What else could you do with PDF documents?</h3>
<ul>
<li>
<p>You can implement some smart question answering solutions over your documents.</p>
</li>
<li>
<p>Gemini can help make sense of differences between two versions of your PDF paper.</p>
</li>
<li>
<p>Gemini allows you to ingest multiple files at the same time,
so it is possible to pass the PDF of your dishwasher manual,
at the same time as a tutorial showing how to repair it,
and then ask the LLM to answer a question on how to fix it.</p>
</li>
</ul>
<h2 id="summary">Summary</h2>
<p>Multimodality is a powerful feature of Gemini,
and now LangChain4j is equiped with the ability to send text, images, audio files, videos, and PDF documents,
potentially all at the same time, to create some innovative multimedia integrations.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Text classification with Gemini and LangChain4j</title><link>https://glaforge.dev/posts/2024/07/11/text-classification-with-gemini-and-langchain4j/</link><pubDate>Thu, 11 Jul 2024 22:26:36 +0200</pubDate><guid>https://glaforge.dev/posts/2024/07/11/text-classification-with-gemini-and-langchain4j/</guid><description>&lt;p>Generative AI has potential applications far beyond chatbots and Retrieval Augmented Generation.
For example, a nice use case is: &lt;strong>text classification&lt;/strong>.&lt;/p>
&lt;p>I had the chance of meeting some customers and prospects who had the need for triaging incoming requests, or for labeling existing data.
In the first case, a government entity was tasked with routing citizen requests to access undisclosed information to the right governmental service that could grant or reject that access. In the second case, a company needed to sort out tons of existing internal documents that were not properly organized, and they wanted to quickly start better structuring this trove of information, by labelling each of these docs into different categories.&lt;/p></description><content:encoded>
<![CDATA[<p>Generative AI has potential applications far beyond chatbots and Retrieval Augmented Generation.
For example, a nice use case is: <strong>text classification</strong>.</p>
<p>I had the chance of meeting some customers and prospects who had the need for triaging incoming requests, or for labeling existing data.
In the first case, a government entity was tasked with routing citizen requests to access undisclosed information to the right governmental service that could grant or reject that access. In the second case, a company needed to sort out tons of existing internal documents that were not properly organized, and they wanted to quickly start better structuring this trove of information, by labelling each of these docs into different categories.</p>
<p>In both situations, the task was a <strong>text classification</strong> one: to put each request or document in a distinct pile, so they could more easily be sorted out, organized, and treated more rapidly.</p>
<p>Before generative AI, text classification would be handled by data scientists who would craft and train dedicated machine learning models for that purpose. But it is now also possible to do the same with the help of large language models.
That&rsquo;s what I&rsquo;d like to explore with you in this article today.</p>
<p>As usual, I&rsquo;ll be using the <a href="https://deepmind.google/technologies/gemini/">Gemini model</a>,
and the <a href="https://docs.langchain4j.dev/">LangChain4j framework</a> for implementing illustrative examples in Java.</p>
<h2 id="text-classification-putting-a-label-on-a-document">Text classification: putting a label on a document</h2>
<p>Before diving into the code, let&rsquo;s step back a short moment to clarify what text classification is about.
When we classify documents, we put a label on them.</p>
<p>For example, in a bug tracker, we could automate adding labels on new tickets that say that the bug report is related to a certain component.
So we would put the name of the component as the label for that new ticket.</p>
<p>For routing incoming document access requests, we could put the label of the service that must treat the request, etc.</p>
<p><strong>Filtering</strong> is also a text classification problem: we can filter the content of emails to state whether they are spam or not.
And we can also use LLMs to filter harmful content from users&rsquo; inputs, and even classify the category of harm (hateful speech, harrasment, etc.)</p>
<h2 id="zero-shot-prompting-just-ask-the-model">Zero-shot prompting: just ask the model!</h2>
<p>What about just asking a large language model what it thinks the classification, or the label should be?
And indeed, LLMs are often very smart and can figure out the correct classification, without being trained specifically for that purpose.</p>
<p>Let&rsquo;s illustrate this with a very common type of text classification: <strong>sentiment analysis</strong>.</p>
<p>First, we can define an <code>enum</code> representing the various sentiments that can be recognized:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">enum</span><span style="color:#bbb"> </span>Sentiment<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>POSITIVE,<span style="color:#bbb"> </span>NEUTRAL,<span style="color:#bbb"> </span>NEGATIVE<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>We create a <code>record</code> which will hold the result of the sentiment analysis:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">SentimentClassification</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Sentiment<span style="color:#bbb"> </span>sentiment<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb"> </span>{}<span style="color:#bbb">
</span></span></span></code></pre></div><p>We will also need an <code>interface</code> to represent the type-safe Java service that the developers integrating this LLM-backed solution will call to retrieve the sentiment of the text:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">interface</span> <span style="color:#0e84b5;font-weight:bold">SentimentClassifier</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>SentimentClassification<span style="color:#bbb"> </span><span style="color:#06287e">classify</span>(String<span style="color:#bbb"> </span>text);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Notice that it takes in input an unstructured <code>String</code> text, but in output, you&rsquo;ll manipulate a strongly typed object, not just a mere string.</p>
<p>It&rsquo;s time to prepare our Gemini model:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(PROJECT_ID)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-pro&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseMimeType</span>(<span style="color:#4070a0">&#34;application/json&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseSchema</span>(Schema.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setType</span>(Type.<span style="color:#4070a0">OBJECT</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">putProperties</span>(<span style="color:#4070a0">&#34;sentiment&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>Schema.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">setType</span>(Type.<span style="color:#4070a0">STRING</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">addAllEnum</span>(Stream.<span style="color:#4070a0">of</span>(Sentiment.<span style="color:#4070a0">values</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">map</span>(Enum::name)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">collect</span>(Collectors.<span style="color:#4070a0">toList</span>()))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>We&rsquo;re taking advantage of the latest feature of Gemini and LangChain4j, which permits to specify that we want 100% valid JSON in output,
and even better than this, we want the generated JSON output to comply with a JSON schema!</p>
<p>Now we create the sentiment analysis service:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>SentimentClassifier<span style="color:#bbb"> </span>sentimentClassifier<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>AiServices.<span style="color:#4070a0">create</span>(SentimentClassifier.<span style="color:#4070a0">class</span>,<span style="color:#bbb"> </span>model);<span style="color:#bbb">
</span></span></span></code></pre></div><p>And we call it to retrieve the sentiment of the text we want to analyze:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>SentimentClassification<span style="color:#bbb"> </span>classification<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>sentimentClassifier.<span style="color:#4070a0">classify</span>(<span style="color:#4070a0">&#34;I am happy!&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(classification.<span style="color:#4070a0">sentiment</span>());<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// POSITIVE</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>We didn&rsquo;t even need to give Gemini examples, this is why it&rsquo;s called <em>zero-shot prompting</em>.
LLMs are usually smart enough to easily handle familiar classification tasks like sentiment analysis.</p>
<h2 id="few-shot-prompting-when-the-model-needs-a-little-help">Few-shot prompting: when the model needs a little help</h2>
<p>A more common approach with LLMs for text classification is <em>few-shot prompting</em>.
As the name implies, it&rsquo;s a prompting technique.</p>
<p>You give the model a task (classifying text), and you show it examples of classifications,
with a clear input/output format, to force the LLM to reply with just the expected class.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ChatLanguageModel<span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(PROJECT_ID)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash-001&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">maxOutputTokens</span>(10)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">maxRetries</span>(3)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>PromptTemplate<span style="color:#bbb"> </span>promptTemplate<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>PromptTemplate.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Analyze the sentiment of the text below.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Respond only with one word to describe the sentiment.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    INPUT: This is fantastic news!
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    OUTPUT: POSITIVE
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    INPUT: Pi is roughly equal to 3.14
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    OUTPUT: NEUTRAL
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    INPUT: I hate disliked the pizza. Who&#39;d put pineapple toppings?
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    OUTPUT: NEGATIVE
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    INPUT: {{text}}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    OUTPUT:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Prompt<span style="color:#bbb"> </span>prompt<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>promptTemplate.<span style="color:#4070a0">apply</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;text&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;I love strawberries!&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(prompt.<span style="color:#4070a0">toUserMessage</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response.<span style="color:#4070a0">content</span>().<span style="color:#4070a0">text</span>());<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// POSITIVE</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>In the above approach, we use LangChain4j&rsquo;s <code>PromptTemplate</code>, with a placeholder value <code>{{text}}</code> that will contain the text to classify.
We don&rsquo;t use an <code>enum</code> value though, so we have to discriminate against a string in the end.
But we could also apply the same schema response handling as in our previous zero-shot example.</p>
<p>Let&rsquo;s rewrite this code a little bit differently, to <em>fake</em> a conversation with the model.
The model will see an exchange between a user and itself, and will also follow the same syntax, and will reply with just one word: the sentiment.
We&rsquo;ll use system instructions, and alternating AI and user messages:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>List<span style="color:#666">&lt;</span>ChatMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>fewShotPrompts<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>List.<span style="color:#4070a0">of</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>SystemMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Analyze the sentiment of the text below.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Respond only with one word to describe the sentiment.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;This is fantastic news!&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>AiMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;POSITIVE&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;Pi is roughly equal to 3.14&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>AiMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;NEUTRAL&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;I hate disliked the pizza. &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                     </span><span style="color:#4070a0">&#34;Who&#39;d put pineapple toppings?&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>AiMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;NEGATIVE&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;I love strawberries!&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(fewShotPrompts);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response.<span style="color:#4070a0">content</span>().<span style="color:#4070a0">text</span>());<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// POSITIVE</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>Same outcome, stawberries are yummy!</p>
<h2 id="text-classification-with-embedding-models">Text classification with embedding models</h2>
<p>In the two previous sections, we took advantage of LLMs&rsquo; abilities to classify text on their own,
based on their intrinsic knowledge, or with the help of a few examples.
But there&rsquo;s another way we can investigate: <strong>using embedding vectors</strong> to compare texts.</p>
<p>Embedding vectors are mathematical representations of words/sentences/paragraphs, in the form of a vector of floating point values.
The way those vectors are calculated by <em>embedding models</em> makes those vector close to each other
(in terms of distance) when they are semantically close.
You can have a look at my recent article
<a href="https://glaforge.dev/posts/2024/07/02/the-power-of-embeddings-how-numbers-unlock-the-meaning-of-data/">introducing vector embeddings</a>.</p>
<p>LangChain4j provides a <code>TextClassifier</code> interface which allows to classify text, by comparing it to sets of other texts
that belong to a same class. So we give a map of possible labels, associated with lists of texts that belong to that category.</p>
<p>In particular, there&rsquo;s an <code>EmbeddingModelTextClassifier</code> that uses embedding models to compare the texts with the examples of each labels.
We can even tweak its internal algorithm to say whether we prefer if a text should be closer to the average of all the examples,
or if we prefer if it&rsquo;s closer to one of the examples (by default, it&rsquo;s half distance to the mean, and half distance to the closest example.)</p>
<p>So let&rsquo;s have a look at this solution.</p>
<p>Instead of doing sentiment analysis, we&rsquo;ll go with recipe classification: our goal will be to classify a recipe,
to know if it&rsquo;s an <em>appetizer</em>, a <em>main course</em>, or a <em>dessert</em>.</p>
<p>First, we need to define our labels, with an <code>enum</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">enum</span><span style="color:#bbb"> </span>DishType<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>APPETIZER,<span style="color:#bbb"> </span>MAIN,<span style="color:#bbb"> </span>DESSERT<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Because we don&rsquo;t have a dataset of recipes, we&rsquo;ll use Gemini to generate sample recipes, for each label.
For that, we need to configure Gemini:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel<span style="color:#bbb"> </span>CHAT_MODEL<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">project</span>(PROJECT_ID)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>We&rsquo;ll also configure an embedding model to calculate the vector embeddings:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>VertexAiEmbeddingModel<span style="color:#bbb"> </span>EMBEDDING_MODEL<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>VertexAiEmbeddingModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">project</span>(PROJECT_ID)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">endpoint</span>(ENDPOINT)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">publisher</span>(<span style="color:#4070a0">&#34;google&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;text-embedding-004&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">taskType</span>(VertexAiEmbeddingModel.<span style="color:#4070a0">TaskType</span>.<span style="color:#4070a0">CLASSIFICATION</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Vertex AI&rsquo;s embedding models are capable of handling various tasks, including:</p>
<ul>
<li><strong>classification</strong>,</li>
<li>semantic similarity,</li>
<li>clustering,</li>
<li>question answering,</li>
<li>fact verification,</li>
<li>query or document retrieval.</li>
</ul>
<p>Let&rsquo;s create a method to generate a recipe for a particular type of dish:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">recipeOf</span>(DishType<span style="color:#bbb"> </span>type)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>CHAT_MODEL.<span style="color:#4070a0">generate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Write a recipe for a %s dish&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">formatted</span>(type.<span style="color:#4070a0">name</span>().<span style="color:#4070a0">toLowerCase</span>()));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>And we&rsquo;ll collect 3 examples of recipes for each type of dish:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>examplesOfRecipes<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Stream.<span style="color:#4070a0">of</span>(DishType.<span style="color:#4070a0">values</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">collect</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Collectors.<span style="color:#4070a0">toMap</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>dishType<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>dishType,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>dishType<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>Stream.<span style="color:#4070a0">generate</span>(()<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>recipeOf(dishType))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">limit</span>(3)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>.<span style="color:#4070a0">toList</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>That way, we have our dataset ready, and we&rsquo;ll prepare a text classifier:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>EmbeddingModelTextClassifier<span style="color:#666">&lt;</span>DishType<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>recipeClassifier<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>EmbeddingModelTextClassifier<span style="color:#666">&lt;&gt;</span>(EMBEDDING_MODEL,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                                       </span>examplesOfRecipes);<span style="color:#bbb">
</span></span></span></code></pre></div><p>It takes a little while to calculate the initial embedding vectors of all the samples, but now our classifier is ready!
Let&rsquo;s see if the following recipe is an <em>appertizer</em>, a <em>main course</em>, or a <em>dessert</em>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>List<span style="color:#666">&lt;</span>DishType<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>classifiedDishes<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>recipeClassifier.<span style="color:#4070a0">classify</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    **Classic Moist Chocolate Cake**
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    This recipe delivers a rich, moist chocolate cake that&#39;s
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    perfect for any occasion.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Ingredients:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    * 1 ¾ cups all-purpose flour
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    * 2 cups granulated sugar
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    * ¾ cup unsweetened cocoa powder
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    * 1 ½ teaspoons baking powder
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    * 1 ½ teaspoons baking soda
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    * 1 teaspoon salt
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    * 2 large eggs
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    * 1 cup milk
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    * ½ cup vegetable oil
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    * 2 teaspoons vanilla extract
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    * 1 cup boiling water
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Instructions:
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    * Preheat oven to 350°F (175°C). Grease and flour two 9-inch
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      round cake pans.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    * Combine dry ingredients: In a large bowl, whisk together flour,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      sugar, cocoa powder, baking powder, baking soda, and salt.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    * Add wet ingredients: Beat in eggs, milk, oil, and vanilla until
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      combined.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    * Stir in boiling water: Carefully stir in boiling water. The
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      batter will be thin.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    * Bake: Pour batter evenly into prepared pans. Bake for 30-35
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      minutes, or until a toothpick inserted into the center comes
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      out clean.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    * Cool: Let cakes cool in pans for 10 minutes before transferring
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">      to a wire rack to cool completely.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;This recipe is of type: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>classifiedDishes);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// This recipe is of type: [DESSERT]</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>And voilà, we used the full power of embedding models to calculate text similarity to classify our chocolate cake recipe as a dessert!</p>
<h2 id="conclusion">Conclusion</h2>
<p>Large Language Models like Gemini are great at classifying text, thanks to their general knowledge of the world that they acquired during their training. But for more specialized use cases, we might need to guide the LLM to recognize labels, because the subject is very specific to our data. That&rsquo;s when few-shot prompting or embedding model-based classification helps.</p>
<p>If we have lots of samples for each label, using a few-shot prompting approach means we&rsquo;ll have to pass all those examples again and again in the context window of the LLM, which yields a high token count. So if you pay per tokens, it can become a bit expensive.</p>
<p>If we use the embedding model text classifier, it might take a while to compute all the embedding vectors, but we&rsquo;ll do it only once, and then we can just calculate the vector embedding for the text to classify, so it&rsquo;s just the tokens of the text to classify that is incurred.
If we have lots of samples, the classifier needs to do quite a few vector / matrix computations to calculate the distance to the samples, but it&rsquo;s usually quite fast (unless we really have hundreds or thousands of samples).</p>
<p>I hope this article showed you that Generative AI is useful beyond the usual chatbots and RAG use cases.
It&rsquo;s great at text classification as well.
And LangChain4j and Gemini are well suited for that use case, and you learned how to implement different approaches to do text classification.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Latest Gemini features support in LangChain4j 0.32.0</title><link>https://glaforge.dev/posts/2024/07/05/latest-gemini-features-support-in-langchain4j/</link><pubDate>Fri, 05 Jul 2024 11:53:30 +0200</pubDate><guid>https://glaforge.dev/posts/2024/07/05/latest-gemini-features-support-in-langchain4j/</guid><description>&lt;p>&lt;a href="https://docs.langchain4j.dev/">LangChain4j&lt;/a> 0.32.0 was released yesterday,
including my &lt;a href="https://github.com/langchain4j/langchain4j/pull/1278">pull request&lt;/a>
with the support for lots of new Gemini features:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>JSON output mode&lt;/strong>, to force Gemini to reply using JSON, without any markup,&lt;/li>
&lt;li>&lt;strong>JSON schema&lt;/strong>, to control and constrain the JSON output to comply with a schema,&lt;/li>
&lt;li>&lt;strong>Response grounding&lt;/strong> with Google Search web results and with private data in Vertex AI datastores,&lt;/li>
&lt;li>Easier debugging, thanks to new builder methods to &lt;strong>log requests and responses&lt;/strong>,&lt;/li>
&lt;li>&lt;strong>Function calling mode&lt;/strong> (none, automatic, or a subset of functions),&lt;/li>
&lt;li>&lt;strong>Safety settings&lt;/strong> to catch harmful prompts and responses.&lt;/li>
&lt;/ul>
&lt;p>Let&amp;rsquo;s explore those new features together, thanks to some code examples!
And at the end of the article, if you make it through, you&amp;rsquo;ll also discover &lt;strong>2 extra bonus points&lt;/strong>.&lt;/p></description><content:encoded>
<![CDATA[<p><a href="https://docs.langchain4j.dev/">LangChain4j</a> 0.32.0 was released yesterday,
including my <a href="https://github.com/langchain4j/langchain4j/pull/1278">pull request</a>
with the support for lots of new Gemini features:</p>
<ul>
<li><strong>JSON output mode</strong>, to force Gemini to reply using JSON, without any markup,</li>
<li><strong>JSON schema</strong>, to control and constrain the JSON output to comply with a schema,</li>
<li><strong>Response grounding</strong> with Google Search web results and with private data in Vertex AI datastores,</li>
<li>Easier debugging, thanks to new builder methods to <strong>log requests and responses</strong>,</li>
<li><strong>Function calling mode</strong> (none, automatic, or a subset of functions),</li>
<li><strong>Safety settings</strong> to catch harmful prompts and responses.</li>
</ul>
<p>Let&rsquo;s explore those new features together, thanks to some code examples!
And at the end of the article, if you make it through, you&rsquo;ll also discover <strong>2 extra bonus points</strong>.</p>
<h2 id="json-output-mode">JSON output mode</h2>
<p>Creating LLM-powered applications means working with text, as this is what LLMs return.
But to facilitate this integration between LLM responses and your code,
the text format of choice is usually JSON, as it&rsquo;s human-readable, and easy to parse programmatically.</p>
<p>However, LLMs are a bit chatty, and rather than sending you back a nice raw JSON document, instead, it replies with some extra sentence, and some markdown markup to wrap the piece of JSON.</p>
<p>Fortunately, Gemini 1.5 (Flash and Pro) allows you to specify the response MIME type.
Currently, only <code>application/json</code> is supported, but other formats may come later.</p>
<p>To do that, when instantiating the Gemini model, use the <code>responseMimeType()</code> builder method:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(PROJECT_ID)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseMimeType</span>(<span style="color:#4070a0">&#34;application/json&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(<span style="color:#4070a0">&#34;Roll a dice&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response);<span style="color:#bbb">
</span></span></span></code></pre></div><p>No sentence, no markdown markup, nothing, just pure JSON:</p>
<pre tabindex="0"><code>{&#34;roll&#34;: 3}
</code></pre><p>We didn&rsquo;t even need to say in the prompt we wanted to get a JSON response!</p>
<p>However, the JSON key of that document may vary from time to time, so you may still wish to be a bit more prescriptive in your prompt, and ask the model to return JSON explicitly, give it an example of the JSON output you expect, etc. That&rsquo;s the usual prompting approach&hellip;</p>
<p>But now there&rsquo;s more!</p>
<h2 id="json-schema-output">JSON Schema output</h2>
<p>This is quite unique in the LLM ecosystem, as I believe it&rsquo;s the only model out there that allows you to specify a JSON schema for constraining the JSON output.
This works for Gemini 1.5 Pro only, not with Gemini 1.5 Flash.</p>
<p>Let&rsquo;s have another look at our previous dice roll example, and let&rsquo;s update it to specify a JSON schema for the output generation:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import static</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.model.vertexai.SchemaHelper.fromClass</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">DiceRoll</span>(<span style="color:#902000">int</span><span style="color:#bbb"> </span>roll)<span style="color:#bbb"> </span>{}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(<span style="color:#4070a0">&#34;genai-java-demos&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(<span style="color:#4070a0">&#34;us-central1&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-pro&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseSchema</span>(fromClass(DiceRoll.<span style="color:#4070a0">class</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(<span style="color:#4070a0">&#34;Roll a dice&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response);<span style="color:#bbb">
</span></span></span></code></pre></div><p>The generated JSON document will always contain the <code>roll</code> key</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ <span style="color:#062873;font-weight:bold">&#34;roll&#34;</span>: <span style="color:#40a070">5</span> }
</span></span></code></pre></div><p>In this example, we used a convenience method called <code>fromClass()</code> that creates a JSON schema that corresponds to a Java type (here a Java record).</p>
<p>But there&rsquo;s also another convenient method that lets us pass a JSON schema string, called <code>fromJsonSchema()</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(<span style="color:#4070a0">&#34;genai-java-demos&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(<span style="color:#4070a0">&#34;us-central1&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-pro&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseSchema</span>(fromJsonSchema(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;type&#34;: &#34;object&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            &#34;properties&#34;: {
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                &#34;roll&#34;: {
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                    &#34;type&#34;: &#34;integer&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                }
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            }
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        }
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>It&rsquo;s also possible to construct a JSON schema programmatically:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(<span style="color:#4070a0">&#34;genai-java-demos&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(<span style="color:#4070a0">&#34;us-central1&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-pro&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">responseSchema</span>(Schema.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setType</span>(Type.<span style="color:#4070a0">OBJECT</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">putProperties</span>(<span style="color:#4070a0">&#34;roll&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>Schema.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">setType</span>(Type.<span style="color:#4070a0">INTEGER</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Now you always get consistent JSON outputs!</p>
<h2 id="response-grounding-with-google-search-web-results-and-vertex-ai-datastores">Response grounding with Google Search web results and Vertex AI datastores</h2>
<p>Large Language Models are wonderful creative machines, but rather than benefiting from their high degree of creativity, we&rsquo;d prefer having factual responses grounded on data and documents.</p>
<p>Gemini offers the ability to <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/ground-gemini">ground responses</a>:</p>
<ul>
<li>against Google Search web results,</li>
<li>against Vertex AI search datastores.</li>
</ul>
<h3 id="use-google-search-to-ground-responses">Use Google Search to ground responses</h3>
<p>The training of an LLM ended at a certain date: its <em>cut-off</em> date.
So it doesn&rsquo;t know about news that happened after that date.
But you can request Gemini to use Google Search to find more up-to-date information.</p>
<p>For example, if we ask Gemini about the current elections going on in France, it could reply with something like this:</p>
<pre tabindex="0"><code>There is no current national election happening in France right now.

The last major national election in France was the **Presidential
election in April and May 2022**, where Emmanuel Macron won a second
term.

There are, however, **local elections** happening regularly in
different regions of France.

To stay updated on French elections, you can check the website of
the **French Ministry of the Interior** or reputable news sources
like **The Guardian, BBC, CNN, or Le Monde**.
</code></pre><p>Now, let&rsquo;s enable the use of Google Search web result with the <code>useGoogleSearch(true)</code> method:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(PROJECT_ID)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">useGoogleSearch</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;What is the current election going on in France?&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response);<span style="color:#bbb">
</span></span></span></code></pre></div><p>The answer will be much different, and indeed factual and up-to-date:</p>
<pre tabindex="0"><code>France held the first round of a parliamentary election on July 4,
2024. The second round will be on July 7, 2024. The election is
significant because it could result in the first far-right government
in France since World War II.  The National Rally, President Emmanuel
Macron’s centrist alliance, and the New Popular Front coalition are
the three major political blocs competing in the election. The
outcome of the election is highly uncertain, with the far-right
National Rally potentially gaining a parliamentary majority.  If the
National Rally wins a majority, Macron would be expected to appoint
Jordan Bardella, the party&#39;s president, as prime minister.
</code></pre><p>There&rsquo;s indeed a parliamentary election going on right now in France.
Those elections were decided only a month ago, thus past the cut-of-date of the knowledge of the model.</p>
<blockquote>
<p>For my French audience, don&rsquo;t forget to go voting next Sunday!</p></blockquote>
<h3 id="grounding-with-vertex-ai-search">Grounding with Vertex AI Search</h3>
<p>The idea is that we want to ground responses on our own data.
This is particularly important when the knowledge required is actually private information, like our internal docs, or our customers&rsquo; docs.</p>
<p>My colleague Mete wrote a great
<a href="https://atamel.dev/posts/2024/07-01_grounding_with_own_data_vertexai_search/">article explaining how to setup grounding with private data</a>.
Below, I&rsquo;ll assume that we created a Vertex AI search app with a datastore backed by a Google Cloud Storage bucket that contains a fictious document which is a car manual, about the <em>Cymbel Starlight</em> car model! I&rsquo;m taking the same example as in Mete&rsquo;s article.</p>
<p>This time, we specify the search location to point at the Vertex AI search datastore with <code>vertexSearchDatastore()</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(PROJECT_ID)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">vertexSearchDatastore</span>(String.<span style="color:#4070a0">format</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;projects/%s/locations/%s/collections/%s/dataStores/%s&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>PROJECT_ID,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;global&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;default_collection&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;cymbal-datastore_1720169982142&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;What is the cargo capacity of Cymbal Starlight?&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response);<span style="color:#bbb">
</span></span></span></code></pre></div><p>It&rsquo;s a fictious car that doesn&rsquo;t exist, but it&rsquo;s covered in that private document, and indeed, Gemini is now able to respond to that question:</p>
<pre tabindex="0"><code>The Cymbal Starlight 2024 has a cargo capacity of 13.5 cubic feet.
</code></pre><p>What&rsquo;s interesting as well is that the response returned by Gemini provides some context about the source document that helped it answer the user query (we&rsquo;ll see in the next section how to enable logging requests and responses):</p>
<pre tabindex="0"><code>  grounding_metadata {
    2: {
      1: {
        3: 66
      }
      2: 0x3f7deee0
    }
    5: {
      2: {
        1: &#34;gs://genai-java-demos-documents/cymbal-starlight-2024.pdf&#34;
        2: &#34;cymbal-starlight-2024&#34;
      }
    }
    6: {
      1: {
        3: 66
        4: &#34;The Cymbal Starlight 2024 has a cargo capacity of 13.5 cubic feet.&#34;
      }
      2: &#34;\000&#34;
      3: {
        257772: 63
      }
    }
</code></pre><p>However, to be honest, I&rsquo;m not quite sure what the numbers exactly mean, but this metadata mentions that the PDF uploaded in cloud storage is the one that was used to shape the answer of the LLM, and gives an excerpt of the sentence that was found in the document.</p>
<h2 id="request-and-response-logging">Request and response logging</h2>
<p>To better understand what&rsquo;s going on under the hood, you can enable request and response logging.
That way, you&rsquo;re able to see exactly what is sent to Gemini, and what Gemini replies.</p>
<p>To enable logging, there are two methods we can use:</p>
<ul>
<li><code>logRequests(true)</code> to log the request sent to Gemini,</li>
<li><code>logResponse(true)</code> to log the response received from Gemini.</li>
</ul>
<p>Let&rsquo;s see that in action:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(PROJECT_ID)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">logRequests</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">logResponses</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(<span style="color:#4070a0">&#34;Why is the sky blue?&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Here&rsquo;s what&rsquo;s logged:</p>
<pre tabindex="0"><code>[main] DEBUG dev.langchain4j.model.vertexai.VertexAiGeminiChatModel -
 GEMINI (gemini-1.5-flash) request: InstructionAndContent {
 systemInstruction = null,
 contents = [role: &#34;user&#34;
parts {
  text: &#34;Why is the sky blue?&#34;
}
]
} tools: []


[main] DEBUG dev.langchain4j.model.vertexai.VertexAiGeminiChatModel -
 GEMINI (gemini-1.5-flash) response: candidates {
  content {
    role: &#34;model&#34;
    parts {
      text: &#34;The sky appears blue due to a phenomenon called
      **Rayleigh scattering**. Here\&#39;s a breakdown:\n\n* **Sunlight
      is made up of all colors of the rainbow.**  When sunlight enters
      the Earth\&#39;s atmosphere, it encounters tiny particles like
      nitrogen and oxygen molecules.\n* **These particles scatter the
      sunlight in all directions.**  However, shorter wavelengths of
      light, like blue and violet, scatter more strongly than longer
      wavelengths, like red and orange.\n* **This preferential
      scattering of shorter wavelengths is called Rayleigh
      scattering.**
      As a result, we see more blue light scattered throughout the sky,
      making it appear blue.\n\n**Why is the sky not violet?**\n\nEven
      though violet light scatters even more strongly than blue, our
      eyes are more sensitive to blue light. This is why we perceive
      the sky as blue rather than violet.\n\n**Other factors that
      affect sky color:**\n\n* **Time of day:** The sky appears more
      red or orange at sunrise and sunset because the sunlight has to
      travel through more of the atmosphere, scattering away most of
      the blue light.\n* **Clouds:** Clouds are made up of larger water
      droplets or ice crystals, which scatter all wavelengths of light
      equally. This is why clouds appear white.\n* **Pollution:**
      Pollution particles can scatter light differently, sometimes
      making the sky appear hazy or even reddish.\n\nLet me know if
      you have any other questions about the sky! \n&#34;
    }
  }
  finish_reason: STOP
  safety_ratings {
    category: HARM_CATEGORY_HATE_SPEECH
    probability: NEGLIGIBLE
    probability_score: 0.054802597
    severity: HARM_SEVERITY_NEGLIGIBLE
    severity_score: 0.03314852
  }
  safety_ratings {
    category: HARM_CATEGORY_DANGEROUS_CONTENT
    probability: NEGLIGIBLE
    probability_score: 0.100348406
    severity: HARM_SEVERITY_NEGLIGIBLE
    severity_score: 0.06359858
  }
  safety_ratings {
    category: HARM_CATEGORY_HARASSMENT
    probability: NEGLIGIBLE
    probability_score: 0.10837755
    severity: HARM_SEVERITY_NEGLIGIBLE
    severity_score: 0.021491764
  }
  safety_ratings {
    category: HARM_CATEGORY_SEXUALLY_EXPLICIT
    probability: NEGLIGIBLE
    probability_score: 0.10338596
    severity: HARM_SEVERITY_NEGLIGIBLE
    severity_score: 0.020410307
  }
}
usage_metadata {
  prompt_token_count: 6
  candidates_token_count: 288
  total_token_count: 294
}
</code></pre><p>Let me give you a bit more details about the logging. LangChain4j uses Slf4j by default for logging.
Request &amp; Response logging is logged at <code>DEBUG</code> level. So we have to configure our logger and/or logger façace accordingly.</p>
<p>In my test project for this article, I configured the following <code>Maven</code> dependencies for <code>Slf4j</code> and the <code>Simple</code> logger:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>org.slf4j<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>slf4j-api<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>2.0.13<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>org.slf4j<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>slf4j-simple<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>2.0.13<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div><p>I created a properties file to configure the loggers: <code>src/main/resources/simplelogger.properties</code>, which contains the following configuration:</p>
<pre tabindex="0"><code>org.slf4j.simpleLogger.defaultLogLevel=debug
org.slf4j.simpleLogger.log.io.grpc.netty.shaded=info
</code></pre><p>I set the default logging level to be <code>debug</code>.
But there&rsquo;s also Netty, the networking library used under the hood by the Gemini Java SDK, that logs at debug level.
So I specified that the logging for this library should only be at <code>info</code> and above, otherwise the output is super chatty.</p>
<h2 id="function-calling-mode">Function calling mode</h2>
<p>So far, when using Gemini for
<a href="https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling">function calling</a>,
the model would decide on its own if a function would be useful to call, and which function to call.</p>
<p>But Gemini introduces the ability to
<a href="https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling#tool-config">control the function or tool choice</a>.</p>
<p>There are 3 options:</p>
<ul>
<li><code>AUTO</code> — The familiar and default mode, where Gemini decides on its own if a function call is necessary and which one should be made,</li>
<li><code>ANY</code> — Allows to specify a subset of functions from all those available, but also forces the model to pick up one of them (only supported by Gemini 1.5 Pro),</li>
<li><code>NONE</code> — Even if tools are defined and available, prevents Gemini to use any of those tools.</li>
</ul>
<p>Let&rsquo;s have a look at this example:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(PROJECT_ID)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-pro&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">logRequests</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">logResponses</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">toolCallingMode</span>(ToolCallingMode.<span style="color:#4070a0">ANY</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">allowedFunctionNames</span>(Arrays.<span style="color:#4070a0">asList</span>(<span style="color:#4070a0">&#34;add&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ToolSpecification<span style="color:#bbb"> </span>adder<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ToolSpecification.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">description</span>(<span style="color:#4070a0">&#34;adds two numbers&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">name</span>(<span style="color:#4070a0">&#34;add&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">addParameter</span>(<span style="color:#4070a0">&#34;a&#34;</span>,<span style="color:#bbb"> </span>JsonSchemaProperty.<span style="color:#4070a0">INTEGER</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">addParameter</span>(<span style="color:#4070a0">&#34;b&#34;</span>,<span style="color:#bbb"> </span>JsonSchemaProperty.<span style="color:#4070a0">INTEGER</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>UserMessage<span style="color:#bbb"> </span>message<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>UserMessage.<span style="color:#4070a0">from</span>(<span style="color:#4070a0">&#34;How much is 3 + 4?&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>AiMessage<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>answer<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(asList(message),<span style="color:#bbb"> </span>adder);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>answer.<span style="color:#4070a0">content</span>().<span style="color:#4070a0">toolExecutionRequests</span>().<span style="color:#4070a0">getFirst</span>());<span style="color:#bbb">
</span></span></span></code></pre></div><p>We specify the <code>ToolCallingMode.ANY</code> mode, and we list the allowed function names of the functions that the model must pick in order to reply to the request (with the <code>allowedFunctionNames()</code> builder method).</p>
<p>We describe the tool that can be called. We create a message.
And when calling <code>generate()</code>, we pass the tool specification corresponding to the function we want to be called.</p>
<p>The output will show that the model replied with the mandatory tool execution request:</p>
<pre tabindex="0"><code>ToolExecutionRequest { id = null, name = &#34;add&#34;,
                       arguments = &#34;{&#34;a&#34;:3.0,&#34;b&#34;:4.0}&#34; }
</code></pre><p>Now it&rsquo;s our turn to call the <code>add</code> function with the arguments.
And then send back the function execution result back to Gemini.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition warning">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>
        <span>Warning</span>
      </div>
      <div class="admonition-content">
        <p>Currently, it is not possible to use the <code>ANY</code> forced function calling mode when using LangChain4j&rsquo;s <code>AiServices</code> class.</p>
<p><code>AiServices</code> takes care of automatic function calling. But the process is a two-step request / response mechanism:</p>
<ul>
<li>First, we ask the model the math question and pass the tool specification along.</li>
<li>The model replies with a <code>ToolExecutionRequest</code>.</li>
<li>Then <code>AiServices</code> makes the function call locally, and replies to the model with the function execution result. However, since the <code>ANY</code> calling mode is specified at the model level, the model still wants to reply with yet another tool execution request. Although at this point, the second call made to the model was <em>just</em> to pass the function execution result, not to request another tool execution.</li>
<li>So <code>AiServices</code> enters an infite loop as the model requests a function execution again and again, not taking into account the execution result that it received.</li>
</ul>
<p>When using <code>AiServices</code>, it&rsquo;s better to let Gemini operate under the default <code>AUTO</code> tool mode.
So it knows when it needs to request a tool execution, or if just needs to handle the tool execution response.</p>
<p>If you want to use the <code>ANY</code> mode with <code>allowedFunctionNames()</code>, then don&rsquo;t use <code>AiServices</code>, and handle the function calls on your own in your code, to avoid such infite loop situations.</p>
      </div>
    </div><h2 id="specify-safety-settings">Specify safety settings</h2>
<p>In LLM-powered applications, where users can enter any kind of weird textual inputs,
you may want to limit harmful content that may be ingested.
To do so, you can specify some safety settings, for different categories of content, with different thresholds of acceptance:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import static</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.model.vertexai.HarmCategory.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import static</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.model.vertexai.SafetyThreshold.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(PROJECT_ID)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">safetySettings</span>(Map.<span style="color:#4070a0">of</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>HARM_CATEGORY_DANGEROUS_CONTENT,<span style="color:#bbb"> </span>BLOCK_LOW_AND_ABOVE,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>HARM_CATEGORY_SEXUALLY_EXPLICIT,<span style="color:#bbb"> </span>BLOCK_MEDIUM_AND_ABOVE,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>HARM_CATEGORY_HARASSMENT,<span style="color:#bbb"> </span>BLOCK_ONLY_HIGH,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>HARM_CATEGORY_HATE_SPEECH,<span style="color:#bbb"> </span>BLOCK_MEDIUM_AND_ABOVE<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>If you want to make your app safer for your end-users, and to avoid malicious or ill-disposed users, that&rsquo;s the way to go!</p>
<h2 id="bonus-point-1-streaming-responses-with-lambda-functions">Bonus point #1: Streaming responses with lambda functions</h2>
<p>I&rsquo;ll round up the review of Gemini-focused features with one little addition I contributed to the project:
the ability to pass a lambda instead of a streaming content handler, when using a streaming model.</p>
<p>This is not Gemini-related, you can use it with any model!</p>
<p>More concretely, if you want to use Gemini or another model in streaming mode, to see the response being printed as it&rsquo;s generated by the model, you would usually write the following code:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiStreamingChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(PROJECT_ID)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>model.<span style="color:#4070a0">generate</span>(<span style="color:#4070a0">&#34;Why is the sky blue?&#34;</span>,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>StreamingResponseHandler<span style="color:#666">&lt;&gt;</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">onNext</span>(String<span style="color:#bbb"> </span>aFewTokens)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">print</span>(aFewTokens);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">onError</span>(Throwable<span style="color:#bbb"> </span>throwable)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">throw</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>RuntimeException(throwable);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>});<span style="color:#bbb">
</span></span></span></code></pre></div><p>Using an anonymous inner class implementing the <code>StreamingResponseHandler</code> interface is quite verbose.
Fortunately, I contributed a couple static methods you can import, to make the code a little bit more concise:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import static</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.model.LambdaStreamingResponseHandler.onNext</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import static</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.model.LambdaStreamingResponseHandler.onNextAndError</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// onNext</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>model.<span style="color:#4070a0">generate</span>(<span style="color:#4070a0">&#34;Why is the sky blue?&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>onNext(System.<span style="color:#4070a0">out</span>::println));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// onNextAndError</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>model.<span style="color:#4070a0">generate</span>(<span style="color:#4070a0">&#34;Why is the sky blue?&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>onNextAndError(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>System.<span style="color:#4070a0">out</span>::println,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>ex<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">throw</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>RuntimeException(ex);<span style="color:#bbb"> </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>));<span style="color:#bbb">
</span></span></span></code></pre></div><p>Now you can stream your LLM output in a single instruction!</p>
<h2 id="bonus-point-2-generating-stunning-images-with-imagen-v3">Bonus point #2: Generating stunning images with Imagen v3</h2>
<p>A second bonus point in this new LangChain4j release is the fact that the Vertex AI Image model now supports
<a href="https://deepmind.google/technologies/imagen-3/">Imagen v3</a> (Google DeepMind&rsquo;s latest high-quality image generation model).</p>

    <div class="admonition warning">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>
        <span>Warning</span>
      </div>
      <div class="admonition-content">
        <p>To use the Imagen model, you&rsquo;ll still have to be allow-listed for now.
You&rsquo;ll need to <a href="https://docs.google.com/forms/d/1cqt9padvfMgqn23W5FMPTqh7bW1KLkEOsC5G6uC-uuM/viewform">fill this form</a>
to request access to the model.</p>
      </div>
    </div><p>There are a few new parameters that are available that you can take advantage of when generating pictures.
Let&rsquo;s have a look at the following image generation code:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>imagenModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiImageModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(PROJECT)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">endpoint</span>(ENDPOINT)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">publisher</span>(<span style="color:#4070a0">&#34;google&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;imagen-3.0-generate-preview-0611&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">aspectRatio</span>(VertexAiImageModel.<span style="color:#4070a0">AspectRatio</span>.<span style="color:#4070a0">LANDSCAPE</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">mimeType</span>(VertexAiImageModel.<span style="color:#4070a0">MimeType</span>.<span style="color:#4070a0">JPEG</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">compressionQuality</span>(80)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">watermark</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// true by default with Imagen v3</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">withPersisting</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">logRequests</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">logResponses</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>prompt<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    An oil painting close-up, with heavy brush strokes full of
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    paint, of two hands shaking together, a young one, and an
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    old one conveying a sense of heartfelt thanks and connection
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    between generations
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>Image<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>imageResponse<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>imagenModel.<span style="color:#4070a0">generate</span>(prompt);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(imageResponse.<span style="color:#4070a0">content</span>().<span style="color:#4070a0">url</span>());<span style="color:#bbb">
</span></span></span></code></pre></div><p>Let&rsquo;s see the resulting picture?</p>
<p><figure>
  <a href="#img-3a5a8cd81b0d62f1c0de3f16aadbc098">
    <img src="/img/gemini/imagen-v3-two-hands-shaking.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-3a5a8cd81b0d62f1c0de3f16aadbc098">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/imagen-v3-two-hands-shaking.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>In the code above, you certainly noticed the new builder methods:</p>
<ul>
<li><code>aspectRatio()</code> — not only square, but wide and narrow landscape and portrait modes are available,</li>
<li><code>mimeType()</code> — in addition to PNG, you can request JPEG image generation,</li>
<li><code>comressionQuality()</code> — when requesting JPEG, you can chose the level of compression for encoding the image,</li>
<li><code>watermark()</code> — to have all your generated images be watermarked with <a href="https://deepmind.google/technologies/synthid/">SynthId</a>,</li>
<li><code>logRequest()</code> / <code>logResponse()</code> — to see what is exchanged with the model, in and out,</li>
<li><code>persistToCloudStorage()</code> — to specify you want the image saved in a cloud storage bucket (not used in this example).</li>
</ul>
<p>If you get a chance, and request access to Imagen v3, you&rsquo;ll notice really great quality improvements compared to v2!</p>
<h2 id="conclusion">Conclusion</h2>
<p>Lots of new Gemini related features in this
<a href="https://github.com/langchain4j/langchain4j/releases/tag/0.32.0">release of LangChain4j</a>!
I hope this article helped you learn about them, and will make you want to use them in your projects.</p>
<p>If you want to go hands-on with Gemini with LangChain4j, don&rsquo;t forget to check out my self-paced codelab:
<a href="https://glaforge.dev/posts/2024/03/27/gemini-codelab-for-java-developers/">Gemini codelabg for Java developers, using LangChain4j</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>The power of embeddings: How numbers unlock the meaning of data</title><link>https://glaforge.dev/posts/2024/07/02/the-power-of-embeddings-how-numbers-unlock-the-meaning-of-data/</link><pubDate>Tue, 02 Jul 2024 09:05:07 +0200</pubDate><guid>https://glaforge.dev/posts/2024/07/02/the-power-of-embeddings-how-numbers-unlock-the-meaning-of-data/</guid><description>&lt;h2 id="prelude">Prelude&lt;/h2>
&lt;blockquote>
&lt;p>As I&amp;rsquo;m focusing a lot on Generative AI, I&amp;rsquo;m curious about how things work under the hood, to better understand what I&amp;rsquo;m using in my gen-ai powered projects.
A topic I&amp;rsquo;d like to focus on more is: &lt;strong>vector embeddings&lt;/strong>, to explain more clearly what they are, how they are calculated, and what you can do with them.&lt;/p>
&lt;p>A colleague of mine, &lt;a href="https://x.com/andreban">André&lt;/a>, was showing me a &lt;a href="https://writer-m4n3dyfjhq-uc.a.run.app/">cool experiment&lt;/a>
he&amp;rsquo;s been working on, to help people prepare an interview, with the help of an AI, to shape the structure of the resulting final article to write.&lt;/p></description><content:encoded>
<![CDATA[<h2 id="prelude">Prelude</h2>
<blockquote>
<p>As I&rsquo;m focusing a lot on Generative AI, I&rsquo;m curious about how things work under the hood, to better understand what I&rsquo;m using in my gen-ai powered projects.
A topic I&rsquo;d like to focus on more is: <strong>vector embeddings</strong>, to explain more clearly what they are, how they are calculated, and what you can do with them.</p>
<p>A colleague of mine, <a href="https://x.com/andreban">André</a>, was showing me a <a href="https://writer-m4n3dyfjhq-uc.a.run.app/">cool experiment</a>
he&rsquo;s been working on, to help people prepare an interview, with the help of an AI, to shape the structure of the resulting final article to write.</p>
<p>The idea is to provide: a topic, a target audience, and to describe the goals for the audience.
Then, a large language model like <a href="https://deepmind.google/technologies/gemini/">Gemini</a> prepares a list of questions (that you can update freely) on that topic.
Next, it&rsquo;s your turn to fill in the blanks, answer those questions, and then the LLM generates an article,
with a plan following those key questions and your provided answers.
I cheated a bit, and asked <a href="https://gemini.google.com/">Gemini</a> itself those questions, and honestly, I really liked how the resulting article came to be,
and I wanted to share with you the outcome below.</p>
<p>It&rsquo;s a great and simple introduction to vector embeddings!
I like how AI can help organize information, shape the structure and the content for an article.
<strong>I&rsquo;m not advocating for letting AI write all your articles</strong>, far from that, but as an author,
however, I like that it can help me avoid the blank page syndrome, avoid missing key elements in my dissertation, improve the quality of my written prose.</p>
<p>Generative AI, in its creative aspect, and as your assistant, can be super useful! Use it as <strong>a tool to help drive your creativity</strong>!
But <strong>always use your critical sense to gauge the quality and factuality of the content</strong>.</p></blockquote>
<h2 id="introduction-what-are-vector-embeddings">Introduction: What are vector embeddings?</h2>
<p>Imagine you have a vast library filled with books on every topic imaginable. Finding a specific book can be a daunting task, especially if you only know the general subject matter. Now imagine a magical system that can understand the meaning of each book and represent it as a unique code. This code, called a vector embedding, can then be used to quickly find the most relevant books based on your search query, even if you only have a vague idea of what you&rsquo;re looking for.</p>
<p>This is the power of vector embeddings. They are essentially numerical representations of complex data, like text, images, or audio, that capture the underlying meaning and relationships within the data. These numerical codes, arranged as vectors, allow computers to process and compare data in a way that mimics human understanding.</p>
<h2 id="from-text-to-numbers-the-journey-of-embedding-creation">From Text to Numbers: The Journey of Embedding Creation</h2>
<p>Creating vector embeddings involves a multi-step process that transforms raw data into meaningful mathematical representations. The journey begins with <strong>data preprocessing</strong>, where the data is cleaned, normalized, and prepared for embedding generation. This might involve tasks like removing irrelevant information, standardizing data formats, and breaking text into individual words or subwords (tokenization).</p>
<p>Next comes the heart of the process: <strong>embedding generation</strong>. This step leverages various techniques and algorithms, such as Word2Vec, GloVe, BERT, and ResNet, to convert each data point into a high-dimensional vector. The specific algorithm chosen depends on the type of data being embedded (text, images, or audio) and the intended application.</p>
<p>For instance, Word2Vec uses a neural network to learn relationships between words by analyzing how they co-occur in large text corpora. This results in vector representations for words, where similar words have similar vectors, capturing semantic relationships. Similarly, for images, convolutional neural networks (CNNs) like ResNet can be used to extract features from images, resulting in vectors that represent the visual content.</p>
<h2 id="vector-databases-the-power-of-storing-and-searching-embeddings">Vector Databases: The Power of Storing and Searching Embeddings</h2>
<p>Once embeddings are generated, they need a dedicated storage system for efficient retrieval and comparison. This is where <strong>vector databases</strong> come into play. Unlike traditional databases designed for structured data, vector databases are optimized for storing and searching high-dimensional vector data.</p>
<p>Vector databases employ specialized indexing techniques, such as Annoy, HNSW, and Faiss, to create efficient data structures that allow for fast similarity search. This means that when a user submits a query (e.g., a search term, an image), the database can quickly find the most similar data points based on the similarity of their vector representations.</p>
<h2 id="embeddings-empower-search-finding-the-needle-in-the-haystack">Embeddings Empower Search: Finding the Needle in the Haystack</h2>
<p>The combination of vector embeddings and vector databases revolutionizes search by enabling <strong>semantic search</strong>. This means that instead of relying solely on keyword matching, search engines can understand the meaning behind the data and find relevant results even if the query doesn&rsquo;t use exact keywords.</p>
<p>For example, imagine searching for &ldquo;a picture of a dog with a hat.&rdquo; Traditional keyword-based search might struggle to find relevant images, as the search term might not match the image description. However, with vector embeddings, the search engine can understand the semantic meaning of the query and find images that contain both a dog and a hat, even if those words are not explicitly mentioned in the image description.</p>
<h2 id="beyond-search-expanding-the-reach-of-embeddings">Beyond Search: Expanding the Reach of Embeddings</h2>
<p>Vector embeddings are not limited to search applications. They have become essential tools in a wide range of fields, including:</p>
<ul>
<li><strong>Retrieval Augmented Generation (RAG):</strong> This technique combines the power of information retrieval and generative models to create more informative and relevant responses. Embeddings are used to find relevant information in large text corpora, which is then used to augment prompts for language models, resulting in more accurate and context-aware outputs.</li>
<li><strong>Data Classification:</strong> Embeddings enable the classification of data points into different categories based on their similarity. This finds application in areas like sentiment analysis, spam detection, object recognition, and music genre classification.</li>
<li><strong>Anomaly Detection:</strong> By representing data points as vectors, anomalies can be identified as data points that are significantly different from the majority. This technique is used in various fields, including network intrusion detection, fraud detection, and industrial sensor monitoring.</li>
</ul>
<h2 id="facing-the-challenges-and-shaping-the-future">Facing the Challenges and Shaping the Future</h2>
<p>While vector embeddings have revolutionized data analysis, they still face some challenges. These include the difficulty of capturing polysemy (multiple meanings of a word), contextual dependence, and the challenge of interpreting the meaning behind the high-dimensional vector representations.</p>
<p>Despite these limitations, research continues to push the boundaries of vector embeddings. Researchers are exploring techniques like contextual embeddings, multilingual embeddings, knowledge graph integration, and explainable embeddings to overcome existing limitations and unlock the full potential of these powerful representations.</p>
<h2 id="stepping-into-the-world-of-embeddings-resources-and-next-steps">Stepping into the World of Embeddings: Resources and Next Steps</h2>
<p>For those interested in diving deeper into the world of vector embeddings, a wealth of resources is available. Online courses and tutorials on platforms like Coursera, Fast.ai, and Stanford&rsquo;s online learning platform provide a solid foundation in the underlying concepts and techniques.</p>
<p>Books like &ldquo;Speech and Language Processing&rdquo; by Jurafsky and Martin and &ldquo;Deep Learning&rdquo; by Goodfellow, Bengio, and Courville offer in-depth coverage of the field. Additionally, research papers and articles on platforms like arXiv and Medium offer insights into the latest advancements and applications.</p>
<p>To gain practical experience, explore Python libraries like Gensim, spaCy, and TensorFlow/PyTorch. These libraries provide tools for creating and working with embeddings, allowing you to build your own models and experiment with various applications.</p>
<p>The world of vector embeddings is constantly evolving, offering exciting opportunities for innovation and discovery. By understanding the power of these representations, you can unlock new possibilities for data analysis, information retrieval, and artificial intelligence applications.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Functional builders in Java with Jilt</title><link>https://glaforge.dev/posts/2024/06/17/functional-builders-in-java-with-jilt/</link><pubDate>Mon, 17 Jun 2024 20:31:25 +0200</pubDate><guid>https://glaforge.dev/posts/2024/06/17/functional-builders-in-java-with-jilt/</guid><description>&lt;p>A few months ago, I shared an article about what I called Java
&lt;a href="https://glaforge.dev/posts/2024/01/16/java-functional-builder-approach/">functional builders&lt;/a>,
inspired by an equivalent pattern found in Go.
The main idea was to have builders that looked like this example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>LanguageModel&lt;span style="color:#bbb"> &lt;/span>languageModel&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">new&lt;/span>&lt;span style="color:#bbb"> &lt;/span>LanguageModel(&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>name(&lt;span style="color:#4070a0">&amp;#34;cool-model&amp;#34;&lt;/span>),&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>project(&lt;span style="color:#4070a0">&amp;#34;my-project&amp;#34;&lt;/span>),&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>temperature(0.&lt;span style="color:#4070a0">5&lt;/span>),&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>description(&lt;span style="color:#4070a0">&amp;#34;This is a generative model&amp;#34;&lt;/span>)&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>);&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Compared to the more tranditional builder approach:&lt;/p>
&lt;ul>
&lt;li>You&amp;rsquo;re using the &lt;code>new&lt;/code> keyword again to construct instances.&lt;/li>
&lt;li>There&amp;rsquo;s no more &lt;code>build()&lt;/code> method, which felt a bit verbose.&lt;/li>
&lt;/ul>
&lt;p>Compared to using constructors with tons of parameters:&lt;/p></description><content:encoded>
<![CDATA[<p>A few months ago, I shared an article about what I called Java
<a href="https://glaforge.dev/posts/2024/01/16/java-functional-builder-approach/">functional builders</a>,
inspired by an equivalent pattern found in Go.
The main idea was to have builders that looked like this example:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>LanguageModel<span style="color:#bbb"> </span>languageModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>LanguageModel(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>name(<span style="color:#4070a0">&#34;cool-model&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>project(<span style="color:#4070a0">&#34;my-project&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>temperature(0.<span style="color:#4070a0">5</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>description(<span style="color:#4070a0">&#34;This is a generative model&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Compared to the more tranditional builder approach:</p>
<ul>
<li>You&rsquo;re using the <code>new</code> keyword again to construct instances.</li>
<li>There&rsquo;s no more <code>build()</code> method, which felt a bit verbose.</li>
</ul>
<p>Compared to using constructors with tons of parameters:</p>
<ul>
<li>You have methods like in traditional builders, that say what each parameter is about (<code>name()</code>, <code>temperature()</code>&hellip;)
a bit similar to named parameters in some programming languages.</li>
</ul>
<p>The approach I followed was to take advantage of lambda functions under the hood:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>ModelOption<span style="color:#bbb"> </span><span style="color:#06287e">temperature</span>(Float<span style="color:#bbb"> </span>temperature)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">temperature</span><span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>temperature;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>However, there were a few downsides:</p>
<ul>
<li>Of course, it&rsquo;s not very conventional! So it can be a bit disturbing for people used to classical builders.</li>
<li>I didn&rsquo;t make the distinction between required and optional parameters (they were all optional!)</li>
<li>The internal fields were not <code>final</code>, and I felt they should be.</li>
</ul>
<h2 id="discovering-jilt">Discovering Jilt</h2>
<p>When searching on this topic, I found <a href="https://x.com/adam_ruka">Adam Ruka</a>&rsquo;s great annotation processor library:
<a href="https://github.com/skinny85/jilt">Jilt</a>.</p>
<p>One of the really cool features of Jilt is its staged builder concept,
which makes builders very type-safe, and forces you to call all the required property methods by chaining them.
I found this approach very elegant.</p>
<p>Adam heard about my functional builder approach, and decided to implement this new style of builder in Jilt.
There are a few differences with my implementation, but it palliates some of the downsides I mentioned.</p>
<p>Let&rsquo;s have a look at what functional builders looks like from a usage standpoint:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>LanguageModel<span style="color:#bbb"> </span>languageModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>languageModel(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>name(<span style="color:#4070a0">&#34;cool-model&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>project(<span style="color:#4070a0">&#34;my-project&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>temperature(0.<span style="color:#4070a0">5</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>description(<span style="color:#4070a0">&#34;This is a generative model&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Compared to my approach, you&rsquo;re not using constructors (as annotation processors can&rsquo;t change existing classes),
so you have to use a static method instead. But otherwise, inside that method call,
you have the named-parameter-like methods you&rsquo;re used to use in builders.</p>
<p>Here, <code>name()</code>, <code>project()</code> and <code>temperature()</code> are mandatory, and you&rsquo;d get a compilation error if you forgot one of them.
But <code>description()</code> is optional and can be ommitted.</p>
<p>Let&rsquo;s now look at the implementation:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">org.jilt.Builder</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">org.jilt.BuilderStyle</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">org.jilt.Opt</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import static</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">jilt.testing.LanguageModelBuilder.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import static</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">jilt.testing.LanguageModelBuilder.Optional.description</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>LanguageModel<span style="color:#bbb"> </span>languageModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>languageModel(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>name(<span style="color:#4070a0">&#34;cool-model&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>project(<span style="color:#4070a0">&#34;my-project&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>temperature(0.<span style="color:#4070a0">5</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>description(<span style="color:#4070a0">&#34;This is a generative model&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#555;font-weight:bold">@Builder</span>(style<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>BuilderStyle.<span style="color:#4070a0">FUNCTIONAL</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">LanguageModel</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>name,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>project,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Double<span style="color:#bbb"> </span>temperature,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Opt</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>description<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb"> </span>{}<span style="color:#bbb">
</span></span></span></code></pre></div><p>I used a Java <code>record</code> but it could be a good old POJO.
You must annotate that class with the <code>@Builder</code> annotation.
The <code>style</code> parameter specifies that you want to use a <em>functional</em> builder.
Notice the use of the <code>@Opt</code> annotation to say that a parameter is not required.</p>
<h2 id="derived-instance-creation">Derived instance creation</h2>
<p>Let me close this article with another neat trick offered by Jilt, which is how to build other instances from existing ones:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Builder</span>(style<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>BuilderStyle.<span style="color:#4070a0">FUNCTIONAL</span>,<span style="color:#bbb"> </span>toBuilder<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;derive&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">record</span> <span style="color:#0e84b5;font-weight:bold">LanguageModel</span>(...)<span style="color:#bbb"> </span>{}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>LanguageModel<span style="color:#bbb"> </span>derivedModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>derive(languageModel,<span style="color:#bbb"> </span>name(<span style="color:#4070a0">&#34;new-name&#34;</span>));<span style="color:#bbb">
</span></span></span></code></pre></div><p>By adding the <code>toBuilder = &quot;derive&quot;</code> parameter to the annotation, you get the ability to create new instances
similar to the original one, but you can change both required and optional parameters, to derive a new instance.</p>
<h2 id="time-to-try-jilt">Time to try Jilt!</h2>
<p>You can try functional builders in <a href="https://github.com/skinny85/jilt">Jilt 1.6</a> which was just released a few days ago!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Let's make Gemini Groovy!</title><link>https://glaforge.dev/posts/2024/06/03/lets-make-gemini-groovy/</link><pubDate>Mon, 03 Jun 2024 11:49:26 +0200</pubDate><guid>https://glaforge.dev/posts/2024/06/03/lets-make-gemini-groovy/</guid><description>&lt;p>The happy users of &lt;a href="https://gemini.google.com/advanced">Gemini Advanced&lt;/a>,
the powerful AI web assistant powered by the Gemini model,
can execute some Python code, thanks to a built-in Python interpreter.
So, for math, logic, calculation questions, the assistant can let Gemini invent a Python script,
and execute it, to let users get a more accurate answer to their queries.&lt;/p>
&lt;p>But wearing my &lt;a href="https://groovy-lang.org/">Apache Groovy&lt;/a> hat on,
I wondered if I could get Gemini to invoke some Groovy scripts as well, for advanced math questions!&lt;/p></description><content:encoded>
<![CDATA[<p>The happy users of <a href="https://gemini.google.com/advanced">Gemini Advanced</a>,
the powerful AI web assistant powered by the Gemini model,
can execute some Python code, thanks to a built-in Python interpreter.
So, for math, logic, calculation questions, the assistant can let Gemini invent a Python script,
and execute it, to let users get a more accurate answer to their queries.</p>
<p>But wearing my <a href="https://groovy-lang.org/">Apache Groovy</a> hat on,
I wondered if I could get Gemini to invoke some Groovy scripts as well, for advanced math questions!</p>
<h2 id="langchain4j-based-approach">LangChain4j based approach</h2>
<p>As usual, my tool of choice for any LLM problem is the powerful <a href="https://docs.langchain4j.dev/">LangChain4j</a> framework!
Interestingly, there are already some code engine integrations,</p>
<ul>
<li>a <a href="https://www.graalvm.org/latest/reference-manual/polyglot-programming/">GraalVM Polyglot Truffle</a> engine, that can execute Python and JavaScript code,</li>
<li>a <a href="https://judge0.com/">Judge0</a> engine that uses the Judge0 online code execution system, which also supports Groovy!</li>
</ul>
<p>I haven&rsquo;t tried Judge0 yet, as I saw it was supporting Groovy 3 only, and not yet Groovy 4.
But for math or logic questions, Groovy 3 is just fine anyway.
Instead, I wanted to explore how to create my own Groovy interpreter!</p>
<p>In the following experiment, I&rsquo;m going to use the <a href="https://deepmind.google/technologies/gemini/">Gemini</a> model,
because it supports <em>function calling</em>, which means we can instruct the model that it can use some tools when needed.</p>
<p>Let&rsquo;s walk through this step by step.</p>
<p>First, I instantiate a Gemini chat model:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(<span style="color:#4070a0">&#34;MY_GCP_PROJECT_ID&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(<span style="color:#4070a0">&#34;us-central1&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash-001&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">maxRetries</span>(1)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Then, I create a tool that is able to run Groovy code, thanks to the <code>GroovyShell</code> evaluator:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">GroovyInterpreter</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#555;font-weight:bold">@Tool</span>(<span style="color:#4070a0">&#34;Execute a Groovy script and return the result of its execution.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>Map<span style="color:#666">&lt;</span>String,<span style="color:#bbb"> </span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span><span style="color:#06287e">executeGroovyScript</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@P</span>(<span style="color:#4070a0">&#34;The groovy script source code to execute&#34;</span>)<span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>groovyScript)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>script<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>groovyScript.<span style="color:#4070a0">replace</span>(<span style="color:#4070a0">&#34;\\n&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;\n&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>System.<span style="color:#4070a0">err</span>.<span style="color:#4070a0">format</span>(<span style="color:#4070a0">&#34;%n--&gt; Executing the following Groovy script:%n%s%n&#34;</span>,<span style="color:#bbb"> </span>script);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>Object<span style="color:#bbb"> </span>result<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GroovyShell().<span style="color:#4070a0">evaluate</span>(script);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;result&#34;</span>,<span style="color:#bbb"> </span>result<span style="color:#bbb"> </span><span style="color:#666">==</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span><span style="color:#666">?</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;null&#34;</span><span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>result.<span style="color:#4070a0">toString</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">catch</span><span style="color:#bbb"> </span>(Throwable<span style="color:#bbb"> </span>e)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;error&#34;</span>,<span style="color:#bbb"> </span>e.<span style="color:#4070a0">getMessage</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Notice the <code>@Tool</code> annotation that describes what this tool can do.
And the <code>@P</code> annotation which explains what the parameter is about.</p>
<p>I noticed that sometimes the raw script that Gemini suggested contained some <code>\n</code> strings,
instead of the plain newline characters, so I&rsquo;m replacing them with newlines instead.</p>
<p>I return a map containing either a result (as a string), or an error message if one was encountered.</p>
<p>Now it&rsquo;s time to create our assistant contract, in the form of an interface,
but with a very carefully crafted system instruction:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">interface</span> <span style="color:#0e84b5;font-weight:bold">GroovyAssistant</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#555;font-weight:bold">@SystemMessage</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    You are a problem solver equipped with the capability of \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    executing Groovy scripts.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    When you need to or you&#39;re asked to evaluate some math \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    function, some algorithm, or some code, use the \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    `executeGroovyScript` function, passing a Groovy script \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    that implements the function, the algorithm, or the code \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    that needs to be run.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    In the Groovy script, return a value. Don&#39;t print the result \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    to the console.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Don&#39;t use semicolons in your Groovy scripts, it&#39;s not necessary.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    When reporting the result of the execution of a script, \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    be sure to show the content of that script.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Call the `executeGroovyScript` function only once, \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    don&#39;t call it in a loop.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>String<span style="color:#bbb"> </span><span style="color:#06287e">chat</span>(String<span style="color:#bbb"> </span>msg);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>This complex system instruction above tells the model what its role is,
and that it should call the provided Groovy script execution function
whenever it encounters the need to calculate some function, or execute some logic.</p>
<p>I also instruct it to return values instead of printing results.</p>
<p>Funnily, Gemini is a pretty decent Groovy programmer,
but it insists on always adding semi-colons like in Java,
so for a more <em>idiomatic</em> code style, I suggest it to get rid of them!</p>
<p>The final step is now to create our LangChain4j AI service with the following code:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>assistant<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>AiServices.<span style="color:#4070a0">builder</span>(GroovyAssistant.<span style="color:#4070a0">class</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">chatLanguageModel</span>(model)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">chatMemory</span>(MessageWindowChatMemory.<span style="color:#4070a0">withMaxMessages</span>(20))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">tools</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GroovyInterpreter())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>I combine the Gemini chat model, with a memory to keep track of users&rsquo; requests,
and the Groovy interpreter tool I&rsquo;ve just created.</p>
<p>Now let&rsquo;s see if Gemini is able to create and calculate a fibonacci function:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>assistant.<span style="color:#4070a0">chat</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;Write a `fibonacci` function, and calculate `fibonacci(18)`&#34;</span>));<span style="color:#bbb">
</span></span></span></code></pre></div><p>And the output is as follows:</p>
<blockquote>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> <span style="color:#06287e">fibonacci</span><span style="color:#666">(</span>n<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>n <span style="color:#666">&lt;=</span> <span style="color:#40a070">1</span><span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">return</span> n
</span></span><span style="display:flex;"><span>  <span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">else</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">return</span> <span style="color:#06287e">fibonacci</span><span style="color:#666">(</span>n <span style="color:#666">-</span> <span style="color:#40a070">1</span><span style="color:#666">)</span> <span style="color:#666">+</span> fibonacci<span style="color:#666">(</span>n <span style="color:#666">-</span> <span style="color:#40a070">2</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>  <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>fibonacci<span style="color:#666">(</span><span style="color:#40a070">18</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>The result of executing the script is: 2584.</p></blockquote>
<h2 id="discussion">Discussion</h2>
<p>It took me a bit of time to find the right system instruction to get Groovy scripts that complied to my requirements.
However, I noticed sometimes some internal errors returned by the model, which I haven&rsquo;t fully understood
(and particularly why those happen at all)</p>
<p>On some occasions, I also noticed that LangChain4j keeps sending the same script for execution, in a loop.
Same thing: I still have to investigate why this rare behavior happens.</p>
<p>So this solution is a fun experiment, but I&rsquo;d call it just that, an experiment, as it&rsquo;s not as rock-solid as I want it to be.
But if I manage to make it more bullet-proof, maybe I could contribute it back as a dedicated execution engine for LangChain4j!</p>
<h2 id="full-source-code">Full source code</h2>
<p>Here&rsquo;s the full content of my experiment:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.agent.tool.P</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.agent.tool.Tool</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.memory.chat.MessageWindowChatMemory</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.model.vertexai.VertexAiGeminiChatModel</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.service.AiServices</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.service.SystemMessage</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">groovy.lang.GroovyShell</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.Map</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">GroovyCodeInterpreterAssistant</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">main</span>(String<span style="color:#666">[]</span><span style="color:#bbb"> </span>args)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">project</span>(<span style="color:#4070a0">&#34;MY_GCP_PROJECT_ID&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">location</span>(<span style="color:#4070a0">&#34;us-central1&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash-001&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">maxRetries</span>(1)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">GroovyInterpreter</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#555;font-weight:bold">@Tool</span>(<span style="color:#4070a0">&#34;Execute a Groovy script and return the result of its execution.&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>Map<span style="color:#666">&lt;</span>String,<span style="color:#bbb"> </span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span><span style="color:#06287e">executeGroovyScript</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#555;font-weight:bold">@P</span>(<span style="color:#4070a0">&#34;The groovy script source code to execute&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>String<span style="color:#bbb"> </span>groovyScript)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>System.<span style="color:#4070a0">err</span>.<span style="color:#4070a0">format</span>(<span style="color:#4070a0">&#34;%n--&gt; Raw Groovy script:%n%s%n&#34;</span>,<span style="color:#bbb"> </span>groovyScript);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>script<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>groovyScript.<span style="color:#4070a0">replace</span>(<span style="color:#4070a0">&#34;\\n&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;\n&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>System.<span style="color:#4070a0">err</span>.<span style="color:#4070a0">format</span>(<span style="color:#4070a0">&#34;%n--&gt; Executing:%n%s%n&#34;</span>,<span style="color:#bbb"> </span>script);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>Object<span style="color:#bbb"> </span>result<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GroovyShell().<span style="color:#4070a0">evaluate</span>(script);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;result&#34;</span>,<span style="color:#bbb"> </span>result<span style="color:#bbb"> </span><span style="color:#666">==</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span><span style="color:#bbb"> </span><span style="color:#666">?</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;null&#34;</span><span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>result.<span style="color:#4070a0">toString</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">catch</span><span style="color:#bbb"> </span>(Throwable<span style="color:#bbb"> </span>e)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Map.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;error&#34;</span>,<span style="color:#bbb"> </span>e.<span style="color:#4070a0">getMessage</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">interface</span> <span style="color:#0e84b5;font-weight:bold">GroovyAssistant</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#555;font-weight:bold">@SystemMessage</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        You are a problem solver equipped with the capability of \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        executing Groovy scripts.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        When you need to or you&#39;re asked to evaluate some math \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        function, some algorithm, or some code, use the \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        `executeGroovyScript` function, passing a Groovy script \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        that implements the function, the algorithm, or the code \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        that needs to be run.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        In the Groovy script, return a value. Don&#39;t print the result \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        to the console.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Don&#39;t use semicolons in your Groovy scripts, it&#39;s not necessary.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        When reporting the result of the execution of a script, \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        be sure to show the content of that script.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Call the `executeGroovyScript` function only once, \
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        don&#39;t call it in a loop.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>String<span style="color:#bbb"> </span><span style="color:#06287e">chat</span>(String<span style="color:#bbb"> </span>msg);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>assistant<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>AiServices.<span style="color:#4070a0">builder</span>(GroovyAssistant.<span style="color:#4070a0">class</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">chatLanguageModel</span>(model)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">chatMemory</span>(MessageWindowChatMemory.<span style="color:#4070a0">withMaxMessages</span>(20))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">tools</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GroovyInterpreter())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>assistant.<span style="color:#4070a0">chat</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;Write a `fibonacci` function, and calculate `fibonacci(18)`&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Grounding Gemini with Web Search results in LangChain4j</title><link>https://glaforge.dev/posts/2024/05/28/grounding-gemini-with-web-search-in-langchain4j/</link><pubDate>Tue, 28 May 2024 07:42:43 +0200</pubDate><guid>https://glaforge.dev/posts/2024/05/28/grounding-gemini-with-web-search-in-langchain4j/</guid><description>&lt;p>The latest &lt;a href="https://github.com/langchain4j/langchain4j/releases/tag/0.31.0">release of LangChain4j&lt;/a> (version 0.31) added the capability of &lt;em>grounding&lt;/em> large language models with results from web searches.
There&amp;rsquo;s an integration with
&lt;a href="https://developers.google.com/custom-search/v1/overview">Google Custom Search Engine&lt;/a>,
and also &lt;a href="https://tavily.com/">Tavily&lt;/a>.&lt;/p>
&lt;p>The fact of &lt;em>grounding&lt;/em> an LLM&amp;rsquo;s response with the results from a search engine
allows the LLM to find relevant information about the query from web searches,
which will likely include up-to-date information that the model won&amp;rsquo;t have seen
during its training, past its cut-off date when the training ended.&lt;/p></description><content:encoded>
<![CDATA[<p>The latest <a href="https://github.com/langchain4j/langchain4j/releases/tag/0.31.0">release of LangChain4j</a> (version 0.31) added the capability of <em>grounding</em> large language models with results from web searches.
There&rsquo;s an integration with
<a href="https://developers.google.com/custom-search/v1/overview">Google Custom Search Engine</a>,
and also <a href="https://tavily.com/">Tavily</a>.</p>
<p>The fact of <em>grounding</em> an LLM&rsquo;s response with the results from a search engine
allows the LLM to find relevant information about the query from web searches,
which will likely include up-to-date information that the model won&rsquo;t have seen
during its training, past its cut-off date when the training ended.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Remark</span>
      </div>
      <div class="admonition-content">
        <p>Gemini has a built-in <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/grounding/overview#ground-public">Google Web Search grounding</a>
capability, however, LangChain4j&rsquo;s Gemini integration doesn&rsquo;t yet surface this feature.
I&rsquo;m currently working on a pull request to support this.</p>
      </div>
    </div><h2 id="asking-questions-to-your-website">Asking questions to your website</h2>
<p>An interesting use case for LLM web search grounding is for example if you want to search a particular website.
I was interested in asking questions related to articles that I have posted on my personal website and blog.
Let&rsquo;s see, step by step, how you can implement this.</p>
<h3 id="creating-a-custom-search-engine">Creating a custom search engine</h3>
<p>First of all, as I decided to use Google Custom Search, I created a new custom search engine.
I won&rsquo;t detail the steps involved in this process, as it&rsquo;s explained in the <a href="https://developers.google.com/custom-search/docs/tutorial/creatingcse">documentation</a>.
I created a custom search searching only the content on my website: <a href="https://glaforge.dev">glaforge.dev</a>.
But you can potentially search the whole internet if you wish, or just your company website, etc.</p>
<p>Google Custom Search gave me an API key, as well as a Custom Search ID (csi) for my newly created custom search engine.
You can test the custom search engine with that ID with this URL:
<a href="https://programmablesearchengine.google.com/controlpanel/overview?cx=YOUR_CSI_HERE">https://programmablesearchengine.google.com/controlpanel/overview?cx=YOUR_CSI_HERE</a>.
It gives you a Google Search-like interface where you can enter your queries.
There&rsquo;s also a widget that you can integrate in your website if you wish.</p>
<h3 id="implementation">Implementation</h3>
<p>First of all, I configure the chat model I want to use.
I&rsquo;m using the latest and fastest Gemini model: <a href="https://deepmind.google/technologies/gemini/flash/">Gemini 1.5 Flash</a>.
I&rsquo;ve saved my Google Cloud project ID and locaction in environment variables.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>VertexAiGeminiChatModel<span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash-001&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Next, I configure my web search engine.
Here, I&rsquo;m using Google Search, but it could be Tavily as well.
I also saved my API key and the ID of my custom web search in environment variables:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>WebSearchEngine<span style="color:#bbb"> </span>webSearchEngine<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleCustomWebSearchEngine.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GOOGLE_CUSTOM_SEARCH_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">csi</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GOOGLE_CUSTOM_SEARCH_CSI&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//    .logRequests(true)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//    .logResponses(true)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Note that you can log the requests and responses, for debugging purpose.</p>
<p>Next, I define a <em>content retriever</em>, this is a way to let LangChain4j know
that <em>content</em> can be <em>retrieved</em> from a particular tool or location:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ContentRetriever<span style="color:#bbb"> </span>contentRetriever<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>WebSearchContentRetriever.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">webSearchEngine</span>(webSearchEngine)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">maxResults</span>(3)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Now, I define the contract I want to use to interact with my Gemini model, by creating my own custom search <code>interface</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">interface</span> <span style="color:#0e84b5;font-weight:bold">SearchWebsite</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span><span style="color:#06287e">search</span>(String<span style="color:#bbb"> </span>query);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>This interface will be implemented by LangChain4j&rsquo;s <code>AiServices</code> system that binds several components together:
the chat language model (here, Gemini), and the web search content retriever I created above:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>SearchWebsite<span style="color:#bbb"> </span>website<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>AiServices.<span style="color:#4070a0">builder</span>(SearchWebsite.<span style="color:#4070a0">class</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">chatLanguageModel</span>(model)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">contentRetriever</span>(contentRetriever)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Then I can ask my question to the LLM, which will find the relevant information in my blog:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>website.<span style="color:#4070a0">search</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;How can I call the Gemma model from LangChain4j?&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;response = &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>response);<span style="color:#bbb">
</span></span></span></code></pre></div><p>If I comment out the line <code>contentRetriever(contentRetriever)</code>, Gemini does a best effort at answering my question,
but since there&rsquo;s nothing in its training data (before its cut-off date)
about how to call the <a href="https://blog.google/technology/developers/gemma-open-models/">Gemma</a> model from LangChain4j,
it is not able to provide a useful answer.</p>
<p>But with the web search content retriever, Gemini is able to find the right material to ground its answer,
as the custom search returns my article on
<a href="https://glaforge.dev/posts/2024/04/04/calling-gemma-with-ollama-and-testcontainers/">calling Gemma with Ollama, Testcontainers, and LangChain4j</a>:</p>
<pre tabindex="0"><code>Based on the provided information, you can call the Gemma model from
LangChain4j using the following approach:

1. **Use Ollama:** The articles highlight Ollama as a tool for
interacting with Gemma. You would need to set up Ollama and ensure it
has access to the Gemma model.
2. **Integrate TestContainers:** TestContainers helps you manage
containerized environments for testing. You can use it to run Ollama
within a container alongside LangChain4j.
3. **Utilize LangChain4j:** LangChain4j provides the framework for
interacting with large language models. You would define your prompt,
send it to Ollama (which runs Gemma), and receive the response back
through LangChain4j.

**Example Steps:**

1. **Set up Ollama:** Install Ollama and configure it to use the
Gemma model.
2. **Create a Dockerfile:** Use a Dockerfile to define an image that
includes Ollama and any dependencies.
3. **Run Ollama in a container using TestContainers:** Start the
container using TestContainers and ensure it is accessible from your
LangChain4j code.
4. **Implement LangChain4j calls:** Use LangChain4j to construct your
prompt and send it to Ollama (which will pass it to Gemma).
5. **Receive and process the response:** Receive the generated response
from Gemma and process it as needed in your Java application.

**Note:** These steps provide a general approach. You will need to
refer to the documentation for Ollama, TestContainers, and LangChain4j
for specific implementation details.

This method leverages Ollama as an intermediary to access Gemma.
If you have access to Google&#39;s Gemini model directly, you might be
able to integrate it with LangChain4j without the Ollama step,
depending on the specific API or SDK offered by Google.
</code></pre><p>The LLM found that I have to use <a href="https://ollama.com/">Ollama</a> and
<a href="https://testcontainers.com/">TestContainers</a>, as explained in my article.
This information wasn&rsquo;t part of my query, so it proves that it really found the info in the article.</p>
<h2 id="discussion">Discussion</h2>
<p>The LLM based its answer on the <em>excerpts</em> contained in the search results,
not the whole content of the article, so some aspects of this answer are not totally correct:
For instance, you don&rsquo;t have to <em>install</em> Ollama or create your own <em>Dockerfile</em>.</p>
<p>To make the response perfect, I believe we would have to combine web search results
with Retrieval Augmented Generation, or pass the whole context of the article to the model,
so that it could provide a more thorough and factual answer.</p>
<p>For different queries that lead to shorter answers, the answer would probably be more to the point.</p>
<p>Another approach is to annotate our <code>String search(String query)</code> method with a <code>@SystemInstruction()</code>
with instructions that encourage the LLM to provide a shorter answer.
But it&rsquo;s difficult to find the right balance between too long and too short,
and of course without any sort of hallucinations!</p>
<p>For example, you can try with the following system instruction:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">interface</span> <span style="color:#0e84b5;font-weight:bold">SearchWebsite</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#555;font-weight:bold">@SystemMessage</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Provide a paragraph-long answer, not a long step by step explanation.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    Reply with &#34;I don&#39;t know the answer&#34; if the provided information isn&#39;t relevant.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>String<span style="color:#bbb"> </span><span style="color:#06287e">search</span>(String<span style="color:#bbb"> </span>query);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>I got the following response:</p>
<pre tabindex="0"><code>The provided information mentions using Gemma with Ollama,
TestContainers, and LangChain4j. You can use Ollama, a local
LLM server, and TestContainers, which provides lightweight,
disposable containers, to set up a testing environment.
Then, with LangChain4j, a Java library for interacting with LLMs,
you can call Gemma through the Ollama server.
</code></pre><p>Which is shorter and more factual, without being too short either!</p>
<h2 id="whats-next">What&rsquo;s next?</h2>
<p>In an upcoming article, I&rsquo;ll show you how to use Gemini&rsquo;s built-in Google Search grounding,
but first, I have to finish my pull request for the LangChain4j project!</p>
<p>Or I can explore how to reply more precisely to queries that lead to complex answers like the above,
maybe combinging a RAG approach to get the full context of the article found by the web search.</p>
<p>Also, the Tavily API seems to be able to return the raw content of the article,
so maybe it can help giving the LLM the full context of the article to base its answers on it.
So that may be worth comparing those two web search integrations too.</p>
<p>Stay tuned!</p>
<h2 id="full-sample-code">Full sample code</h2>
<p>For reference, here is the full sample (with the system instruction approach):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.model.vertexai.VertexAiGeminiChatModel</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.rag.content.retriever.ContentRetriever</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.rag.content.retriever.WebSearchContentRetriever</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.service.AiServices</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.service.SystemMessage</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.web.search.WebSearchEngine</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">dev.langchain4j.web.search.google.customsearch.GoogleCustomWebSearchEngine</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">GroundingWithSearch</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">main</span>(String<span style="color:#666">[]</span><span style="color:#bbb"> </span>args)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>VertexAiGeminiChatModel<span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiGeminiChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">project</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;PROJECT_ID&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">location</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;LOCATION&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini-1.5-flash-001&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>WebSearchEngine<span style="color:#bbb"> </span>webSearchEngine<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GoogleCustomWebSearchEngine.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">apiKey</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GOOGLE_CUSTOM_SEARCH_API_KEY&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">csi</span>(System.<span style="color:#4070a0">getenv</span>(<span style="color:#4070a0">&#34;GOOGLE_CUSTOM_SEARCH_CSI&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//    .logRequests(true)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//    .logResponses(true)</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>ContentRetriever<span style="color:#bbb"> </span>contentRetriever<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>WebSearchContentRetriever.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">webSearchEngine</span>(webSearchEngine)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">maxResults</span>(3)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">interface</span> <span style="color:#0e84b5;font-weight:bold">SearchWebsite</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#555;font-weight:bold">@SystemMessage</span>(<span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Provide a paragraph-long answer, not a long step by step explanation.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        Reply with &#34;I don&#39;t know the answer&#34; if the provided information isn&#39;t relevant.
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>String<span style="color:#bbb"> </span><span style="color:#06287e">search</span>(String<span style="color:#bbb"> </span>query);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>SearchWebsite<span style="color:#bbb"> </span>website<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>AiServices.<span style="color:#4070a0">builder</span>(SearchWebsite.<span style="color:#4070a0">class</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">chatLanguageModel</span>(model)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">contentRetriever</span>(contentRetriever)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>website.<span style="color:#4070a0">search</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#4070a0">&#34;How can I call the Gemma model from LangChain4j?&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;response = &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>response);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Gemini, Google's Large Language Model, for Java Developers</title><link>https://glaforge.dev/talks/2024/05/03/gemini-google-large-language-model-for-java-developers/</link><pubDate>Fri, 03 May 2024 09:35:08 +0200</pubDate><guid>https://glaforge.dev/talks/2024/05/03/gemini-google-large-language-model-for-java-developers/</guid><description>&lt;p>As a follow-up to my talk on &lt;a href="https://glaforge.dev/talks/2023/11/13/gen-ai-with-palm-2-and-java/">generative AI for Java developers&lt;/a>, I&amp;rsquo;ve developed a new presentation that focuses more on
the &lt;a href="https://deepmind.google/technologies/gemini/#introduction">Gemini&lt;/a> large multimodal model by Google.&lt;/p>
&lt;p>In this talk, we cover the multimodality capabilities of the model, as it&amp;rsquo;s able to ingest code, PDF, audio, video, and is able to reason about them.
Another specificity of Gemini is its huge context window of up to 1 million tokens!
This opens interesting perspectives, especially in multimodal scenarios.&lt;/p></description><content:encoded>
<![CDATA[<p>As a follow-up to my talk on <a href="https://glaforge.dev/talks/2023/11/13/gen-ai-with-palm-2-and-java/">generative AI for Java developers</a>, I&rsquo;ve developed a new presentation that focuses more on
the <a href="https://deepmind.google/technologies/gemini/#introduction">Gemini</a> large multimodal model by Google.</p>
<p>In this talk, we cover the multimodality capabilities of the model, as it&rsquo;s able to ingest code, PDF, audio, video, and is able to reason about them.
Another specificity of Gemini is its huge context window of up to 1 million tokens!
This opens interesting perspectives, especially in multimodal scenarios.</p>
<p>We also talk about the <a href="https://blog.google/technology/developers/gemma-open-models/">Gemma</a> model, a small open-weights model in the Gemini family, which I covered recently about how to <a href="https://glaforge.dev/posts/2024/04/04/calling-gemma-with-ollama-and-testcontainers/">run it locally thanks to Ollama and Testcontainers</a>.</p>
<p>In that presentation, I&rsquo;m showing some of my past Gemini-powered demos, as well as the code examples in my <a href="https://glaforge.dev/posts/2024/03/27/gemini-codelab-for-java-developers/">Gemini workshop for Java developers</a>, using <a href="https://docs.langchain4j.dev/">LangChain4j</a>.</p>
<script async class="speakerdeck-embed" data-id="202b1956e3b747afa85cbf5d1b40bf20" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Calling Gemma with Ollama, TestContainers, and LangChain4j</title><link>https://glaforge.dev/posts/2024/04/04/calling-gemma-with-ollama-and-testcontainers/</link><pubDate>Wed, 03 Apr 2024 19:02:01 +0200</pubDate><guid>https://glaforge.dev/posts/2024/04/04/calling-gemma-with-ollama-and-testcontainers/</guid><description>&lt;p>Lately, for my Generative AI powered Java apps,
I&amp;rsquo;ve used the &lt;a href="https://deepmind.google/technologies/gemini/#introduction">Gemini&lt;/a>
multimodal large language model from Google.
But there&amp;rsquo;s also &lt;a href="https://blog.google/technology/developers/gemma-open-models/">Gemma&lt;/a>,
its little sister model.&lt;/p>
&lt;p>Gemma is a family of lightweight, state-of-the-art open models built from the same research
and technology used to create the Gemini models. Gemma is available in two sizes: 2B and 7B.
Its weights are freely available, and its small size means you can run it on your own, even on your laptop.
So I was curious to give it a run with &lt;a href="https://docs.langchain4j.dev/">LangChain4j&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>Lately, for my Generative AI powered Java apps,
I&rsquo;ve used the <a href="https://deepmind.google/technologies/gemini/#introduction">Gemini</a>
multimodal large language model from Google.
But there&rsquo;s also <a href="https://blog.google/technology/developers/gemma-open-models/">Gemma</a>,
its little sister model.</p>
<p>Gemma is a family of lightweight, state-of-the-art open models built from the same research
and technology used to create the Gemini models. Gemma is available in two sizes: 2B and 7B.
Its weights are freely available, and its small size means you can run it on your own, even on your laptop.
So I was curious to give it a run with <a href="https://docs.langchain4j.dev/">LangChain4j</a>.</p>
<h2 id="how-to-run-gemma">How to run Gemma</h2>
<p>There are many ways to run Gemma: in the cloud,
via <a href="https://console.cloud.google.com/vertex-ai/publishers/google/model-garden/335">Vertex AI</a>
with a click of a button,
or <a href="https://cloud.google.com/kubernetes-engine/docs/tutorials/serve-gemma-gpu-vllm">GKE</a> with some GPUs,
but you can also run it locally with <a href="https://github.com/tjake/Jlama">Jlama</a> or
<a href="https://github.com/google/gemma.cpp">Gemma.cpp</a>.</p>
<p>Another good option is to run Gemma with <a href="https://ollama.com/">Ollama</a>,
a tool that you install on your machine, and which lets you run small models,
like Llama 2, Mistral, and <a href="https://ollama.com/library">many others</a>.
They quickly added support for <a href="https://ollama.com/library/gemma">Gemma</a> as well.</p>
<p>Once installed locally, you can run:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ollama run gemma:2b
</span></span><span style="display:flex;"><span>ollama run gemma:7b
</span></span></code></pre></div><p>Cherry on the cake, the <a href="">LangChain4j</a> library provides an
<a href="https://docs.langchain4j.dev/integrations/language-models/ollama">Ollama module</a>,
so you can plug Ollama supported models in your Java applications easily.</p>
<h2 id="containerization">Containerization</h2>
<p>After a great discussion with my colleague <a href="https://twitter.com/ddobrin">Dan Dobrin</a>
who had worked with Ollama and TestContainers
(<a href="https://github.com/GoogleCloudPlatform/serverless-production-readiness-java-gcp/blob/main/sessions/next24/books-genai-vertex-langchain4j/src/test/java/services/OllamaContainerTest.java">#1</a> and
<a href="https://github.com/GoogleCloudPlatform/serverless-production-readiness-java-gcp/blob/main/sessions/next24/books-genai-vertex-langchain4j/src/test/java/services/OllamaChatModelTest.java#L37">#2</a>)
in his <a href="https://github.com/GoogleCloudPlatform/serverless-production-readiness-java-gcp/tree/main">serverless production readiness workshop</a>, I decided to try the approach below.</p>
<p>Which brings us to the last piece of the puzzle:
Instead of having to install and run Ollama on my computer,
I decided to use Ollama within a container, handled by <a href="https://testcontainers.com/">TestContainers</a>.</p>
<p>TestContainers is not only useful for testing, but you can also use it for driving containers.
There&rsquo;s even a specific <a href="https://java.testcontainers.org/modules/ollama/">OllamaContainer</a> you can take advantage of!</p>
<p>So here&rsquo;s the whole picture:
<figure>
  <a href="#img-f2b4d2e607831def484498e9d744add3">
    <img src="/img/gemini/gemma-ollama-testcontainers-langchain4j.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-f2b4d2e607831def484498e9d744add3">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/gemma-ollama-testcontainers-langchain4j.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h2 id="time-to-implement-this-approach">Time to implement this approach!</h2>
<p>You&rsquo;ll find the code in the Github
<a href="https://github.com/glaforge/gemini-workshop-for-java-developers/blob/main/app/src/main/java/gemini/workshop/CallGemma.java">repository</a>
accompanying my recent <a href="https://codelabs.developers.google.com/codelabs/gemini-java-developers">Gemini workshop</a></p>
<p>Let&rsquo;s start with the easy part, interacting with an Ollama supported model with LangChain4j:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>OllamaContainer<span style="color:#bbb"> </span>ollama<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>createGemmaOllamaContainer();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ollama.<span style="color:#4070a0">start</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ChatLanguageModel<span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>OllamaChatModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">baseUrl</span>(String.<span style="color:#4070a0">format</span>(<span style="color:#4070a0">&#34;http://%s:%d&#34;</span>,<span style="color:#bbb"> </span>ollama.<span style="color:#4070a0">getHost</span>(),<span style="color:#bbb"> </span>ollama.<span style="color:#4070a0">getFirstMappedPort</span>()))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemma:2b&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(<span style="color:#4070a0">&#34;Why is the sky blue?&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(response);<span style="color:#bbb">
</span></span></span></code></pre></div><ul>
<li>You run an Ollama test container.</li>
<li>You create an Ollama chat model, by pointing at the address and port of the container.</li>
<li>You specify the model you want to use.</li>
<li>Then, you just need to call <code>model.generate(yourPrompt)</code> as usual.</li>
</ul>
<p>Easy?
Now let&rsquo;s have a look at the trickier part, my local method that creates the Ollama container:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// check if the custom Gemma Ollama image exists already</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>List<span style="color:#666">&lt;</span>Image<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>listImagesCmd<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>DockerClientFactory.<span style="color:#4070a0">lazyClient</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">listImagesCmd</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">withImageNameFilter</span>(TC_OLLAMA_GEMMA_2_B)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">exec</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(listImagesCmd.<span style="color:#4070a0">isEmpty</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;Creating a new Ollama container with Gemma 2B image...&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>OllamaContainer<span style="color:#bbb"> </span>ollama<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>OllamaContainer(<span style="color:#4070a0">&#34;ollama/ollama:0.1.26&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>ollama.<span style="color:#4070a0">start</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>ollama.<span style="color:#4070a0">execInContainer</span>(<span style="color:#4070a0">&#34;ollama&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;pull&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;gemma:2b&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>ollama.<span style="color:#4070a0">commitToImage</span>(TC_OLLAMA_GEMMA_2_B);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>ollama;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;Using existing Ollama container with Gemma 2B image...&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// Substitute the default Ollama image with our Gemma variant</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>OllamaContainer(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>DockerImageName.<span style="color:#4070a0">parse</span>(TC_OLLAMA_GEMMA_2_B)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">asCompatibleSubstituteFor</span>(<span style="color:#4070a0">&#34;ollama/ollama&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>You need to create a derived Ollama container that pulls in the Gemma model.
Either this image was already created beforehand, or if it doesn&rsquo;t exist yet, you create it.</p>
<p>Use the Docker Java client to check if the custom Gemma image exists.
If it doesn&rsquo;t exist, notice how TestContainers let you create an image derived from the base Ollama image,
pull the Gemma model, and then commit that image to your local Docker registry.</p>
<p>Otherwise, if the image already exists (ie. you created it in a previous run of the application),
you&rsquo;re just going to tell TestContainers that you want to substitute the default Ollama image
with your Gemma-powered variant.</p>
<h2 id="and-voila">And voila!</h2>
<p>You can <strong>call Gemma locally on your laptop, in your Java apps, using LangChain4j</strong>,
without having to install and run Ollama locally
(but of course, you need to have a Docker daemon running).</p>
<p>Big thanks to <a href="https://twitter.com/ddobrin">Dan Dobrin</a> for the approach,
and to <a href="https://twitter.com/bsideup">Sergei</a>, <a href="https://twitter.com/EdduMelendez">Eddú</a>
and <a href="https://twitter.com/shelajev">Oleg</a> from TestContainers for the help and useful pointers.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Gemini codelab for Java developers using LangChain4j</title><link>https://glaforge.dev/posts/2024/03/27/gemini-codelab-for-java-developers/</link><pubDate>Wed, 27 Mar 2024 19:11:58 +0100</pubDate><guid>https://glaforge.dev/posts/2024/03/27/gemini-codelab-for-java-developers/</guid><description>&lt;p>No need to be a Python developer to do Generative AI!
If you&amp;rsquo;re a Java developer, you can take advantage of &lt;a href="https://docs.langchain4j.dev/">LangChain4j&lt;/a>
to implement some advanced LLM integrations in your Java applications.
And if you&amp;rsquo;re interested in using
&lt;a href="https://blog.google/technology/ai/google-gemini-next-generation-model-february-2024/">Gemini&lt;/a>,
one of the best models available, I invite you to have a look at the following &amp;ldquo;codelab&amp;rdquo; that I worked on:&lt;/p>
&lt;p>&lt;a href="https://codelabs.developers.google.com/codelabs/gemini-java-developers">Codelab — Gemini for Java Developers using LangChain4j&lt;/a>&lt;/p>
&lt;p>In this workshop, you&amp;rsquo;ll find various examples covering the following use cases, in &lt;em>crescendo&lt;/em> approach:&lt;/p></description><content:encoded>
<![CDATA[<p>No need to be a Python developer to do Generative AI!
If you&rsquo;re a Java developer, you can take advantage of <a href="https://docs.langchain4j.dev/">LangChain4j</a>
to implement some advanced LLM integrations in your Java applications.
And if you&rsquo;re interested in using
<a href="https://blog.google/technology/ai/google-gemini-next-generation-model-february-2024/">Gemini</a>,
one of the best models available, I invite you to have a look at the following &ldquo;codelab&rdquo; that I worked on:</p>
<p><a href="https://codelabs.developers.google.com/codelabs/gemini-java-developers">Codelab — Gemini for Java Developers using LangChain4j</a></p>
<p>In this workshop, you&rsquo;ll find various examples covering the following use cases, in <em>crescendo</em> approach:</p>
<ul>
<li>Making your fist call to Gemini (streaming &amp; non-streaming)</li>
<li>Maintaining a conversation</li>
<li>Taking advantage of multimodality by analysing images with your prompts</li>
<li>Extracting structured information from unstructured text</li>
<li>Using prompt templates</li>
<li>Doing text classification with few-shot prompting</li>
<li>Implementing Retrieval Augmented Generation to chat with your documentation</li>
<li>How to do Function Calling to expand the LLM to interact with external APIs and services</li>
</ul>
<p>You&rsquo;ll find all the <a href="https://github.com/glaforge/gemini-workshop-for-java-developers">code samples on Github</a>.</p>
<p>If you&rsquo;re attending Devoxx France, be sure to attend the
<a href="https://www.devoxx.fr/en/schedule/talk/?id=40285">Hands-on-Lab workshop</a> with my colleagues
<a href="https://twitter.com/meteatamel">Mete Atamel</a> and <a href="https://twitter.com/val_deleplace">Valentin Deleplace</a>
who will guide you through this codelab.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Visualize PaLM-based LLM tokens</title><link>https://glaforge.dev/posts/2024/02/05/visualize-palm-based-llm-tokens/</link><pubDate>Mon, 05 Feb 2024 09:44:22 +0100</pubDate><guid>https://glaforge.dev/posts/2024/02/05/visualize-palm-based-llm-tokens/</guid><description>&lt;p>As I was working on tweaking the Vertex AI text embedding model in &lt;a href="https://github.com/langchain4j">LangChain4j&lt;/a>,
I wanted to better understand how the &lt;code>textembedding-gecko&lt;/code>
&lt;a href="https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/text-embeddings">model&lt;/a>
tokenizes the text, in particular when we implement the
&lt;a href="https://arxiv.org/abs/2005.11401">Retrieval Augmented Generation&lt;/a> approach.&lt;/p>
&lt;p>The various PaLM-based models offer a &lt;code>computeTokens&lt;/code> endpoint, which returns a list of tokens (encoded in Base 64)
and their respective IDs.&lt;/p>
&lt;link rel="stylesheet" href="https://glaforge.dev/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&amp;#43;AD&amp;#43;9EeYXUTm&amp;#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
&lt;div class="admonition note">
&lt;div class="admonition-header">&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">&lt;path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/>&lt;/svg>
&lt;span>Note&lt;/span>
&lt;/div>
&lt;div class="admonition-content">
&lt;p>At the time of this writing, there&amp;rsquo;s no equivalent endpoint for Gemini models.&lt;/p></description><content:encoded>
<![CDATA[<p>As I was working on tweaking the Vertex AI text embedding model in <a href="https://github.com/langchain4j">LangChain4j</a>,
I wanted to better understand how the <code>textembedding-gecko</code>
<a href="https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/text-embeddings">model</a>
tokenizes the text, in particular when we implement the
<a href="https://arxiv.org/abs/2005.11401">Retrieval Augmented Generation</a> approach.</p>
<p>The various PaLM-based models offer a <code>computeTokens</code> endpoint, which returns a list of tokens (encoded in Base 64)
and their respective IDs.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>At the time of this writing, there&rsquo;s no equivalent endpoint for Gemini models.</p>
      </div>
    </div><p>So I decided to create a <a href="https://tokens-lpj6s2duga-ew.a.run.app/">small application</a> that lets users:</p>
<ul>
<li>input some text,</li>
<li>select a model,</li>
<li>calculate the number of tokens,</li>
<li>and visualize them with some nice pastel colors.</li>
</ul>
<p>The available PaLM-based models are:</p>
<ul>
<li><code>textembedding-gecko</code></li>
<li><code>textembedding-gecko-multilingual</code></li>
<li><code>text-bison</code></li>
<li><code>text-unicorn</code></li>
<li><code>chat-bison</code></li>
<li><code>code-gecko</code></li>
<li><code>code-bison</code></li>
<li><code>codechat-bison</code></li>
</ul>
<p>You can <a href="https://tokens-lpj6s2duga-ew.a.run.app/">try the application</a> online.</p>
<p>And also have a look at the <a href="https://github.com/glaforge/llm-text-tokenization">source code</a> on Github.
It&rsquo;s a <a href="https://micronaut.io/">Micronaut</a> application.
I serve the static assets as explained in my recent
<a href="https://glaforge.dev/posts/2024/01/21/serving-static-assets-with-micronaut/">article</a>.
I deployed the application on <a href="https://cloud.run/">Google Cloud Run</a>,
the easiest way to deploy a container, and let it auto-scale for you.
I did a source based deployment, as explained at the bottom
<a href="https://glaforge.dev/posts/2022/10/24/build-deploy-java-17-apps-on-cloud-run-with-cloud-native-buildpacks-on-temurin/">here</a>.</p>
<p>And <em>voilà</em> I can visualize my LLM tokens!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Image generation with Imagen and LangChain4j</title><link>https://glaforge.dev/posts/2024/02/01/image-generation-with-imagen-and-langchain4j/</link><pubDate>Thu, 01 Feb 2024 09:25:56 +0100</pubDate><guid>https://glaforge.dev/posts/2024/02/01/image-generation-with-imagen-and-langchain4j/</guid><description>&lt;p>This week &lt;a href="https://github.com/langchain4j" title="LangChain4j">LangChain4j&lt;/a>, the LLM orchestration framework for Java developers, released version
&lt;a href="https://github.com/langchain4j/langchain4j/releases/tag/0.26.1" title="0.26.1">0.26.1&lt;/a>, which contains my first significant contribution to the open source project:
&lt;strong>support for the Imagen image generation model&lt;/strong>.&lt;/p>
&lt;p>&lt;strong>Imagen&lt;/strong> is a text-to-image diffusion model that was &lt;a href="https://imagen.research.google/" title="announced">announced&lt;/a> last year.
And it recently upgraded to &lt;a href="https://deepmind.google/technologies/imagen-2/" title="Imagen v2">Imagen v2&lt;/a>, with even higher quality graphics generation.
As I was curious to integrate it in some of my generative AI projects, I thought that would be a great first
&lt;a href="https://github.com/langchain4j/langchain4j/pull/456" title="contribution">contribution&lt;/a> to LangChain4j.&lt;/p></description><content:encoded>
<![CDATA[<p>This week <a href="https://github.com/langchain4j" title="LangChain4j">LangChain4j</a>, the LLM orchestration framework for Java developers, released version
<a href="https://github.com/langchain4j/langchain4j/releases/tag/0.26.1" title="0.26.1">0.26.1</a>, which contains my first significant contribution to the open source project:
<strong>support for the Imagen image generation model</strong>.</p>
<p><strong>Imagen</strong> is a text-to-image diffusion model that was <a href="https://imagen.research.google/" title="announced">announced</a> last year.
And it recently upgraded to <a href="https://deepmind.google/technologies/imagen-2/" title="Imagen v2">Imagen v2</a>, with even higher quality graphics generation.
As I was curious to integrate it in some of my generative AI projects, I thought that would be a great first
<a href="https://github.com/langchain4j/langchain4j/pull/456" title="contribution">contribution</a> to LangChain4j.</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition caution">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>
        <span>Caution</span>
      </div>
      <div class="admonition-content">
        <p>At the time of this writing, image generation is still only for allow-listed accounts.</p>
<p>Furthermore, to run the snippets covered below, you should have an account on Google Cloud Platform,
created a project, configured a billing account, enabled the Vertex AI API,
and authenticated with the gcloud SDK and the command:
<code>gcloud auth application-default login</code>.</p>
      </div>
    </div><p>Now let&rsquo;s dive in how to use Imagen v1 and v2 with LangChain4j in Java!</p>
<h2 id="generate-your-first-images">Generate your first images</h2>
<p>In the following examples, I&rsquo;m using the following constants, to point at my project details, the endpoint, the region, etc:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>ENDPOINT<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;us-central1-aiplatform.googleapis.com:443&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>LOCATION<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;us-central1&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>PROJECT<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;YOUR_PROJECT_ID&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>PUBLISHER<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;google&#34;</span>;<span style="color:#bbb">
</span></span></span></code></pre></div><p>First, we&rsquo;re going to create an instance of the model:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>VertexAiImageModel<span style="color:#bbb"> </span>imagenModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiImageModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">endpoint</span>(ENDPOINT)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">project</span>(PROJECT)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">publisher</span>(PUBLISHER)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;imagegeneration@005&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">maxRetries</span>(2)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">withPersisting</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>There are 2 models you can use:</p>
<ul>
<li><code>imagegeneration@005</code> corresponds to Imagen 2</li>
<li><code>imagegeneration@002</code> is the previous version (Imagen 1)</li>
</ul>
<p>In this article, we&rsquo;ll use both models. Why? Because currently Imagen 2 doesn&rsquo;t support image editing, so we&rsquo;ll have to use Imagen 1 for that purpose.</p>
<p>The configuration above uses <code>withPersisting()</code> to save the generated images in a temporary folder on your system.
If you don&rsquo;t persist the image files, the content of the image is avaiable as Base 64 encoded bytes in the <code>Image</code>s objects returned.
You can also specify <code>persistTo(somePath)</code> to specify a particular directory where you want the generated files to be saved.</p>
<p>Let&rsquo;s create our first image:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Response<span style="color:#666">&lt;</span>Image<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>imageResponse<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>imagenModel.<span style="color:#4070a0">generate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;watercolor of a colorful parrot drinking a cup of coffee&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>The <code>Response</code> object wraps the created <code>Image</code>.
You can get the <code>Image</code> by calling <code>imageResponse.getContent()</code>.
And you can retrieve the URL of the image (if saved locally) with <code>imageResponse.getContent().url()</code>.
The Base 64 encoded bytes can be retrieved with <code>imageResponse.getContent().base64Data()</code></p>
<p>Some other tweaks to the model configuration:</p>
<ul>
<li>Specify the <strong>language</strong> of the prompt: <code>language(&quot;ja&quot;)</code>
(if the language is not officially supported, it&rsquo;s usually translated back to English anyway).</li>
<li>Define a <strong>negative prompt</strong> with things you don&rsquo;t want to see in the picture: <code>negativePrompt(&quot;black feathers&quot;)</code>.</li>
<li>Use a particular <strong>seed</strong> to always generate the same image with the same seed: <code>seed(1234L)</code>.</li>
</ul>
<p>So if you want to generate a picture of a pizza with a prompt in Japanese, but you don&rsquo;t want to have pepperoni and pineapple,
you could configure your model and generate as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>VertexAiImageModel<span style="color:#bbb"> </span>imagenModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiImageModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">endpoint</span>(ENDPOINT)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">project</span>(PROJECT)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">publisher</span>(PUBLISHER)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;imagegeneration@005&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">language</span>(<span style="color:#4070a0">&#34;ja&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">negativePrompt</span>(<span style="color:#4070a0">&#34;pepperoni, pineapple&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">maxRetries</span>(2)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">withPersisting</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>Image<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>imageResponse<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>imagenModel.<span style="color:#4070a0">generate</span>(<span style="color:#4070a0">&#34;ピザ&#34;</span>);<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// pizza</span><span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="image-editing-with-imagen-1">Image editing with Imagen 1</h2>
<p>With Imagen 1, you can <a href="https://cloud.google.com/vertex-ai/docs/generative-ai/image/edit-images?hl=en" title="edit">edit</a> existing images:</p>
<ul>
<li><strong>mask-based editing:</strong> you can specify a mask, a black &amp; white image where the white parts are the corresponding parts of the original image that should be edited,</li>
<li><strong>mask free editing:</strong> where you just give a prompt and let the model figure out what should be edited on its own or following the prompt.</li>
</ul>
<p>When generating and editing with Imagen 1, you can also configure the model to use a particular style (with Imagen 2, you just specify it in the prompt) with <code>sampleImageStyle(VertexAiImageModel.ImageStyle.photograph)</code>:</p>
<ul>
<li><code>photograph</code></li>
<li><code>digital_art</code></li>
<li><code>landscape</code></li>
<li><code>sketch</code></li>
<li><code>watercolor</code></li>
<li><code>cyberpunk</code></li>
<li><code>pop_art</code></li>
</ul>
<p>When editing an image, you may wish to decide how strong or not the modification should be, with <code>.guidanceScale(100)</code>.
Usually, between 0 and 20 or so, it&rsquo;s lightly edited, between 20 and 100 it&rsquo;s getting more impactful edits, and 100 and above it&rsquo;s the maximum edition level.</p>
<p>Let&rsquo;s say I generated an image of a lush forrest (I&rsquo;ll use that as my original image):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>VertexAiImageModel<span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiImageModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">endpoint</span>(ENDPOINT)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">project</span>(PROJECT)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">publisher</span>(PUBLISHER)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;imagegeneration@002&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">seed</span>(19707L)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">sampleImageStyle</span>(VertexAiImageModel.<span style="color:#4070a0">ImageStyle</span>.<span style="color:#4070a0">photograph</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">guidanceScale</span>(100)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">maxRetries</span>(4)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">withPersisting</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>Image<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>forestResp<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generate</span>(<span style="color:#4070a0">&#34;lush forest&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Now I want to edit my forrest to add a small red tree in the bottom of the image.
I&rsquo;m loading a black and white mask image with a white square at the bottom.
And I pass the original image, the mask image, and the modification prompt, to the new <code>edit()</code> method:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>URI<span style="color:#bbb"> </span>maskFileUri<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>getClass().<span style="color:#4070a0">getClassLoader</span>().<span style="color:#4070a0">getResource</span>(<span style="color:#4070a0">&#34;mask.png&#34;</span>).<span style="color:#4070a0">toURI</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>Image<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>compositeResp<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">edit</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>forestResp.<span style="color:#4070a0">content</span>(),<span style="color:#bbb">              </span><span style="color:#60a0b0;font-style:italic">// original image to edit</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>fromPath(Paths.<span style="color:#4070a0">get</span>(maskFileUri)),<span style="color:#bbb">  </span><span style="color:#60a0b0;font-style:italic">// the mask image</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#4070a0">&#34;red trees&#34;</span><span style="color:#bbb">                        </span><span style="color:#60a0b0;font-style:italic">// the new prompt</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p><figure>
  <a href="#img-9210c97687e9e4cf920605a81813d109">
    <img src="/img/gemini/lush-forrest-red-tree.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-9210c97687e9e4cf920605a81813d109">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gemini/lush-forrest-red-tree.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Another kind of editing you can do is to upscale an existing image.
As far as I know, it&rsquo;s only supported for Imagen v1 for now, so we&rsquo;ll continue with that model.</p>
<p>In this example, we&rsquo;ll generate an image of 1024x1024 pixels, and we&rsquo;ll scale it to 4096x4096:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>VertexAiImageModel<span style="color:#bbb"> </span>imagenModel<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiImageModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">endpoint</span>(ENDPOINT)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">project</span>(PROJECT)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">publisher</span>(PUBLISHER)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;imagegeneration@002&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">sampleImageSize</span>(1024)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">withPersisting</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">persistTo</span>(defaultTempDirPath)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">maxRetries</span>(3)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>Image<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>imageResponse<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>imagenModel.<span style="color:#4070a0">generate</span>(<span style="color:#4070a0">&#34;A black bird looking itself in an antique mirror&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>VertexAiImageModel<span style="color:#bbb"> </span>imagenModelForUpscaling<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>VertexAiImageModel.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">endpoint</span>(ENDPOINT)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">location</span>(LOCATION)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">project</span>(PROJECT)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">publisher</span>(PUBLISHER)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;imagegeneration@002&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">sampleImageSize</span>(4096)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">withPersisting</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">persistTo</span>(defaultTempDirPath)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">maxRetries</span>(3)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Response<span style="color:#666">&lt;</span>Image<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>upscaledImageResponse<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>imagenModelForUpscaling.<span style="color:#4070a0">edit</span>(imageResponse.<span style="color:#4070a0">content</span>(),<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#34;</span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>And now you have a much bigger image!</p>
<h2 id="conclusion">Conclusion</h2>
<p>That&rsquo;s about it for image generation and editing with Imagen in LangChain4j today!
Be sure to use LangChain4j v0.26.1 which contains that new integration.
And I&rsquo;m looking forward to seeing the pictures you generate with it!
m</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Serving static assets with Micronaut</title><link>https://glaforge.dev/posts/2024/01/21/serving-static-assets-with-micronaut/</link><pubDate>Sun, 21 Jan 2024 17:23:25 +0100</pubDate><guid>https://glaforge.dev/posts/2024/01/21/serving-static-assets-with-micronaut/</guid><description>&lt;p>My go-to framework when developing Java apps or microservices is
&lt;a href="https://micronaut.io">Micronaut&lt;/a>.
For the apps that should have a web frontend, I rarely use
&lt;a href="https://micronaut-projects.github.io/micronaut-views/latest/guide/">Micronaut Views&lt;/a>
and its templating support.
Instead, I prefer to just &lt;strong>serve static assets&lt;/strong> from my resource folder,
and have some JavaScript framework (usually &lt;a href="https://vuejs.org/">Vue.js&lt;/a>)
to populate my HTML content (often using
&lt;a href="https://shoelace.style/">Shoelace&lt;/a> for its nice Web Components).
However, the &lt;a href="https://docs.micronaut.io/latest/guide/#staticResources">static asset documentation&lt;/a>
is a bit light on explanations.
So, since I always forget how to configure Micronaut to serve static assets,
I thought that would be useful to document this here.&lt;/p></description><content:encoded>
<![CDATA[<p>My go-to framework when developing Java apps or microservices is
<a href="https://micronaut.io">Micronaut</a>.
For the apps that should have a web frontend, I rarely use
<a href="https://micronaut-projects.github.io/micronaut-views/latest/guide/">Micronaut Views</a>
and its templating support.
Instead, I prefer to just <strong>serve static assets</strong> from my resource folder,
and have some JavaScript framework (usually <a href="https://vuejs.org/">Vue.js</a>)
to populate my HTML content (often using
<a href="https://shoelace.style/">Shoelace</a> for its nice Web Components).
However, the <a href="https://docs.micronaut.io/latest/guide/#staticResources">static asset documentation</a>
is a bit light on explanations.
So, since I always forget how to configure Micronaut to serve static assets,
I thought that would be useful to document this here.</p>
<p>In <code>/src/main/resources/application.properties</code>, I&rsquo;m adding the following:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-properties" data-lang="properties"><span style="display:flex;"><span><span style="color:#4070a0">micronaut.router.static-resources.default.paths</span><span style="color:#666">=</span><span style="color:#4070a0">classpath:public</span>
</span></span><span style="display:flex;"><span><span style="color:#4070a0">micronaut.router.static-resources.default.mapping</span><span style="color:#666">=</span><span style="color:#4070a0">/**</span>
</span></span><span style="display:flex;"><span><span style="color:#4070a0">micronaut.router.static-resources.default.enabled</span><span style="color:#666">=</span><span style="color:#4070a0">true</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#4070a0">micronaut.server.cors.enabled</span><span style="color:#666">=</span><span style="color:#4070a0">true</span>
</span></span></code></pre></div><ul>
<li>The first line says that my resources will live in <code>src/main/resources/public/</code>.</li>
<li>The second line means the pattern will match recursively for sub-directories as well.</li>
<li>The <code>enabled</code> flag is to activate static serviing (not strictly needed as it&rsquo;s supposed to be enabled by default).</li>
<li>I also enabled CORS (cross-origin resource sharing).</li>
</ul>
<p>Then in <code>src/main/resources/public/</code>, I&rsquo;ll have my <code>index.html</code> file,
my <code>css</code> and <code>js</code> folders.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Light Mode Bookmarlet</title><link>https://glaforge.dev/posts/2024/01/18/light-mode-bookmarlet/</link><pubDate>Thu, 18 Jan 2024 09:49:01 +0100</pubDate><guid>https://glaforge.dev/posts/2024/01/18/light-mode-bookmarlet/</guid><description>&lt;p>A while ago, my friend Sylvain Wallez shared a little &lt;a href="https://twitter.com/bluxte/status/1729912211882094701" title="bookmarlet">bookmarlet&lt;/a>&lt;br />
on Twitter/X that transforms a dark mode site into light mode.&lt;br />
I know the trend is towards dark mode, but for a lot of people with certain vision issues,&lt;br />
for example with astigmatism like me, certain dark modes can very painful.&lt;/p>
&lt;p>This site about &lt;a href="https://www.allaboutvision.com/digital-eye-strain/is-dark-mode-better-for-eyes/" title="vision">vision&lt;/a>&lt;br />
(and you&amp;rsquo;ll find other similar references) mentions that:&lt;/p>
&lt;blockquote>
&lt;p>People who have myopia or &lt;strong>astigmatism&lt;/strong> also may experience &lt;strong>halation&lt;/strong> (from the word “halo”).&lt;br />
Halation occurs when light spreads past a certain boundary, creating a foggy or blurry appearance.&lt;/p></description><content:encoded>
<![CDATA[<p>A while ago, my friend Sylvain Wallez shared a little <a href="https://twitter.com/bluxte/status/1729912211882094701" title="bookmarlet">bookmarlet</a><br />
on Twitter/X that transforms a dark mode site into light mode.<br />
I know the trend is towards dark mode, but for a lot of people with certain vision issues,<br />
for example with astigmatism like me, certain dark modes can very painful.</p>
<p>This site about <a href="https://www.allaboutvision.com/digital-eye-strain/is-dark-mode-better-for-eyes/" title="vision">vision</a><br />
(and you&rsquo;ll find other similar references) mentions that:</p>
<blockquote>
<p>People who have myopia or <strong>astigmatism</strong> also may experience <strong>halation</strong> (from the word “halo”).<br />
Halation occurs when light spreads past a certain boundary, creating a foggy or blurry appearance.</p></blockquote>
<p>So for certain websites, often with a too strong contrast, I&rsquo;m using the following bookmarklet trick.</p>
<p>Go to your bookmark manager, and save the following bookmarklet (I called mine &ldquo;light mode&rdquo;):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>javascript<span style="color:#666">:</span>(<span style="color:#007020;font-weight:bold">function</span>(){<span style="color:#007020">document</span>.documentElement.style.filter<span style="color:#666">=</span><span style="color:#007020">document</span>.documentElement.style.filter<span style="color:#666">?%</span><span style="color:#40a070">27</span><span style="color:#666">%</span><span style="color:#40a070">27</span><span style="color:#666">:%</span><span style="color:#40a070">27</span>invert(<span style="color:#40a070">100</span><span style="color:#666">%</span>)<span style="color:#666">%</span><span style="color:#40a070">20</span>hue<span style="color:#666">-</span>rotate(<span style="color:#40a070">180</span>deg)<span style="color:#666">%</span><span style="color:#40a070">27</span>})();
</span></span></code></pre></div><p>Now, to pretty print the above code and remove the URL encoded characters, to decypher what it does:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>(<span style="color:#007020;font-weight:bold">function</span> () {
</span></span><span style="display:flex;"><span>  <span style="color:#007020">document</span>.documentElement.style.filter <span style="color:#666">=</span> <span style="color:#007020">document</span>.documentElement.style.filter
</span></span><span style="display:flex;"><span>    <span style="color:#666">?</span> <span style="color:#4070a0">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">:</span> <span style="color:#4070a0">&#34;invert(100%) hue-rotate(180deg)&#34;</span>;
</span></span><span style="display:flex;"><span>})();
</span></span></code></pre></div><p>Two filters are going to be applied to your current web page:</p>
<ul>
<li>First, it will completely <strong>invert</strong> all the colors, like a negative photography</li>
<li>Second, compared to Sylvain, I also add a <strong>hue rotation</strong> of 180 degrees</li>
</ul>
<h2 id="why-the-hue-rotation">Why the hue rotation</h2>
<p>Because the color inversion is also going to shift the colors: a red will become blue, a yellow will be dark blue, a violet will turn pink, etc.<br />
With a hue rotation, we get back the right color, a red is still red, a blue is still blue, etc.<br />
The different however will be in the lightness, as a light blue becomes dark, and a dark green becomes light.<br />
But at least, it&rsquo;s a bit more faithful to the original images.</p>
<p>Here&rsquo;s a picture to highlight the differences.<br />
See how the rainbow picture is transformed:</p>
<p><figure>
  <a href="#img-5c8dad7eb80fc5afff067353a6436f65">
    <img src="/img/misc/invert-hue-roate.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-5c8dad7eb80fc5afff067353a6436f65">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/invert-hue-roate.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h2 id="possible-improvements">Possible improvements</h2>
<p>Perhaps we could avoid applying the filter globally, or at least avoid to apply it somehow to the images, so that they are not affected by those filters.<br />
At least for now, that&rsquo;s good enough for me!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Functional builder approach in Java</title><link>https://glaforge.dev/posts/2024/01/16/java-functional-builder-approach/</link><pubDate>Tue, 16 Jan 2024 08:33:32 +0100</pubDate><guid>https://glaforge.dev/posts/2024/01/16/java-functional-builder-approach/</guid><description>&lt;p>In Java, builders are a pretty classical pattern for creating complex objects with lots of attributes.
A nice aspect of builders is that they help reduce the number of constructors you need to create,
in particular when not all attributes are required to be set (or if they have default values).&lt;/p>
&lt;p>However, I&amp;rsquo;ve always found builders a bit verbose with their &lt;code>newBuilder()&lt;/code> / &lt;code>build()&lt;/code> method combos,
especially when you work with deeply nested object graphs, leading to lines of code of builders of builders of&amp;hellip;&lt;/p></description><content:encoded>
<![CDATA[<p>In Java, builders are a pretty classical pattern for creating complex objects with lots of attributes.
A nice aspect of builders is that they help reduce the number of constructors you need to create,
in particular when not all attributes are required to be set (or if they have default values).</p>
<p>However, I&rsquo;ve always found builders a bit verbose with their <code>newBuilder()</code> / <code>build()</code> method combos,
especially when you work with deeply nested object graphs, leading to lines of code of builders of builders of&hellip;</p>
<p>As I was chatting about those builders with my colleague <a href="https://www.linkedin.com/in/deleplacevalentin/">Valentin</a>,
who is a Go developer, he told me about Golang&rsquo;s functional builder approach.
It&rsquo;s not a very common implementation practice for Java builders, but it&rsquo;s worth revisiting!</p>
<h2 id="first-the-classical-builder">First, the classical builder</h2>
<p>Let&rsquo;s start with an example.
We want to create a builder for a class with a few attributes.
Not all attributes are mandatory, some may have some default values,
and we don&rsquo;t want to create as many constructors as possible combinations of attributes.</p>
<p>Let me introduce you to my <code>SomeModel</code> class:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">SomeModel</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>modelName;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span>Float<span style="color:#bbb"> </span>temperature<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>0.<span style="color:#4070a0">3f</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span>Integer<span style="color:#bbb"> </span>maxOutputTokens<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>100;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// ... possibly many other attribtues</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#06287e">SomeModel</span>(String<span style="color:#bbb"> </span>modelName,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                      </span>Float<span style="color:#bbb"> </span>temperature,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                      </span>Integer<span style="color:#bbb"> </span>maxOutputTokens)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">this</span>.<span style="color:#4070a0">modelName</span><span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>modelName;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">this</span>.<span style="color:#4070a0">temperature</span><span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>temperature;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">this</span>.<span style="color:#4070a0">maxOutputTokens</span><span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>maxOutputTokens;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Creating tons of constructors for the various model configurations can be painful.
Furthermore, some attributes can have the same type, so from a user perspective, it&rsquo;s hard to know which value corresponds to which parameter type.
So creating a builder can reduce that toil.</p>
<p>We could write a static builder class inside <code>SomeModel</code> along the lines of:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">SomeModelBuilder</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>modelName;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span>Float<span style="color:#bbb"> </span>temperature<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>0.<span style="color:#4070a0">3f</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span>Integer<span style="color:#bbb"> </span>maxOutputTokens<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>100;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>SomeModelBuilder<span style="color:#bbb"> </span><span style="color:#06287e">modelName</span>(String<span style="color:#bbb"> </span>modelName)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">this</span>.<span style="color:#4070a0">modelName</span><span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>modelName;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">this</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>SomeModelBuilder<span style="color:#bbb"> </span><span style="color:#06287e">temperature</span>(Float<span style="color:#bbb"> </span>temperature)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">this</span>.<span style="color:#4070a0">temperature</span><span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>temperature;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">this</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>SomeModelBuilder<span style="color:#bbb"> </span><span style="color:#06287e">maxOutputTokens</span>(Integer<span style="color:#bbb"> </span>maxOutputTokens)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">this</span>.<span style="color:#4070a0">maxOutputTokens</span><span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>maxOutputTokens;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">this</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>SomeModel<span style="color:#bbb"> </span><span style="color:#06287e">build</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>SomeModel(modelName,<span style="color:#bbb"> </span>temperature,<span style="color:#bbb"> </span>maxOutputTokens);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Inside <code>SomeModel</code> you would add a method to instantiate a builder:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>SomeModelBuilder<span style="color:#bbb"> </span><span style="color:#06287e">newBuilder</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>SomeModelBuilder();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Then, the user would create a model instance with the builder as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>SomeModel.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">modelName</span>(<span style="color:#4070a0">&#34;gemini&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">temperature</span>(0.<span style="color:#4070a0">2f</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">maxOutputToken</span>(300)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Not too bad.
The are some variations to this approach, like passing the builder in the class&rsquo; constructor,
using setter methods that return <code>this</code>, using or not using final fields, etc.
But they are mostly stylistic variations.</p>
<p>However, I was wondering about this idea of a functional builder&hellip;</p>
<h2 id="existing-functional-approaches-in-java">Existing functional approaches in Java</h2>
<p>I haven&rsquo;t found much litterature on this theme.
There are 2 blog posts
(<a href="https://medium.com/beingprofessional/think-functional-advanced-builder-pattern-using-lambda-284714b85ed5">here</a>
and <a href="https://www.innovect.com/advanced-builder-using-java-8-lambda">there</a>)
that suggest an approach with lambda expressions and <code>Consumer</code>s,
but I find it even more unconventional than the approach I&rsquo;m going to describe further in this article:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>SomeModel<span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>SomeModelBuilder()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">with</span>($<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>$.<span style="color:#4070a0">modelName</span><span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Gemini&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>$.<span style="color:#4070a0">temperature</span><span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>0.<span style="color:#4070a0">4f</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>})<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">with</span>($<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>$.<span style="color:#4070a0">maxOutputTokens</span><span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>100);<span style="color:#bbb">
</span></span></span></code></pre></div><p>You can pass one or more lambdas in chained calls.
It&rsquo;s the end-user who controls how the model is built, not the implementor, so I feel it&rsquo;s less safe.
The use of the <code>$</code> sign is a bit of a syntactical hack to avoid repeating the name of the variable corresponding to the model.
Finally, there&rsquo;s still a builder class after all, and maybe we can find a way to get rid of it.</p>
<p>Let&rsquo;s see what Go has to offer instead, and if we can get some inspiration from it!</p>
<h2 id="the-go-approach">The Go approach</h2>
<p>My colleague <a href="https://www.linkedin.com/in/deleplacevalentin/">Valentin</a> pointed me at Dave Cheney&rsquo;s
<a href="https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis">article</a> on Go&rsquo;s functional option pattern.
There&rsquo;s also a <a href="https://www.youtube.com/watch?v=24lFtGHWxAQ">video</a> available.</p>
<p>The idea is that the class&rsquo; constructor takes function <em>options</em> as a vararg paramter,
that are able to modify the instance that&rsquo;s being built.</p>
<p>Let&rsquo;s illustrate this with the following snippet.</p>
<p>We create a <code>struct</code> that represents our model object like in our Java example:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">package</span> main
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#4070a0">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">type</span> SomeModel <span style="color:#007020;font-weight:bold">struct</span> {
</span></span><span style="display:flex;"><span>    modelName <span style="color:#902000">string</span>
</span></span><span style="display:flex;"><span>    temperature <span style="color:#902000">float32</span>
</span></span><span style="display:flex;"><span>    maxOutputTokens <span style="color:#902000">int</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>We define a method to construct our model, which takes a vararg of options:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">func</span> <span style="color:#06287e">NewModel</span>(options <span style="color:#666">...</span><span style="color:#007020;font-weight:bold">func</span>(<span style="color:#666">*</span>SomeModel)) (<span style="color:#666">*</span>SomeModel) {
</span></span><span style="display:flex;"><span>    m <span style="color:#666">:=</span> SomeModel{<span style="color:#4070a0">&#34;&#34;</span>, <span style="color:#40a070">0.3</span>, <span style="color:#40a070">100</span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">for</span> _, option <span style="color:#666">:=</span> <span style="color:#007020;font-weight:bold">range</span> options {
</span></span><span style="display:flex;"><span>        <span style="color:#06287e">option</span>(<span style="color:#666">&amp;</span>m)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">return</span> <span style="color:#666">&amp;</span>m
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Those options are actually functions that take a model object as parameter.</p>
<p>Now we can create utility methods that create such option functions,
and we pass the value for each field of the <code>struct</code> via the method parameter.
So we have a method for each structure field: model name, temperature and max output tokens:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">func</span> <span style="color:#06287e">modelName</span>(name <span style="color:#902000">string</span>) <span style="color:#007020;font-weight:bold">func</span>(<span style="color:#666">*</span>SomeModel) {
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">return</span> <span style="color:#007020;font-weight:bold">func</span>(m <span style="color:#666">*</span>SomeModel) {
</span></span><span style="display:flex;"><span>        m.modelName = name
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">func</span> <span style="color:#06287e">temperature</span>(temp <span style="color:#902000">float32</span>) <span style="color:#007020;font-weight:bold">func</span>(<span style="color:#666">*</span>SomeModel) {
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">return</span> <span style="color:#007020;font-weight:bold">func</span>(m <span style="color:#666">*</span>SomeModel) {
</span></span><span style="display:flex;"><span>        m.temperature = temp
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">func</span> <span style="color:#06287e">maxOutputTokens</span>(max <span style="color:#902000">int</span>) <span style="color:#007020;font-weight:bold">func</span>(<span style="color:#666">*</span>SomeModel) {
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">return</span> <span style="color:#007020;font-weight:bold">func</span>(m <span style="color:#666">*</span>SomeModel) {
</span></span><span style="display:flex;"><span>        m.maxOutputTokens = max
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Next we can create the model in the following way, by calling the utility methods that return functions
that are able to modify the <code>struct</code>.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">func</span> <span style="color:#06287e">main</span>() {
</span></span><span style="display:flex;"><span>    m <span style="color:#666">:=</span> <span style="color:#06287e">NewModel</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#06287e">modelName</span>(<span style="color:#4070a0">&#34;gemini&#34;</span>),
</span></span><span style="display:flex;"><span>        <span style="color:#06287e">temperature</span>(<span style="color:#40a070">0.5</span>),
</span></span><span style="display:flex;"><span>        <span style="color:#06287e">maxOutputTokens</span>(<span style="color:#40a070">100</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    fmt.<span style="color:#06287e">Println</span>(m)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Notice there&rsquo;s not even a <code>NewBuilder()</code> or <code>Build()</code> method!</p>
<h2 id="lets-implement-our-functional-builder-in-java">Let&rsquo;s implement our functional builder in Java!</h2>
<p>We can follow the same approach in Java.
Instead of Go functions, we&rsquo;ll use Java&rsquo;s lambdas.
Our lambdas will be converted into <code>Consumer</code>s of <code>SomeModel</code>.</p>
<p>So let&rsquo;s recreate our <code>SomeModel</code> class, with the same fields as before.
This time, however, the constructor won&rsquo;t be <code>private</code>, and it&rsquo;ll take a list of options
(lambda expressions that consume instances of <code>SomeModel</code>).
We&rsquo;ll iterate over all of them to execute them:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.function.Consumer</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">SomeModel</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>modelName;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span>Float<span style="color:#bbb"> </span>temperature<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>0.<span style="color:#4070a0">3f</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span>Integer<span style="color:#bbb"> </span>maxOutputTokens<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>100;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#06287e">SomeModel</span>(ModelOption...<span style="color:#bbb"> </span>options)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(Option<span style="color:#bbb"> </span>option<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>options)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>option.<span style="color:#4070a0">accept</span>(<span style="color:#007020;font-weight:bold">this</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span></code></pre></div>
            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Update #1</span>
      </div>
      <div class="admonition-content">
        <p>A neat <a href="https://gist.github.com/edeandrea/27ee1c61f05e640fc6fa1e19b8fb756e">suggestion</a>
by Eric Deandrea on Twitter to use streams to filter the null options,
as I was not checking them with an extra <code>if</code> before calling <code>accept()</code> on the option:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Optional.<span style="color:#4070a0">ofNullable</span>(options)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">   </span>.<span style="color:#4070a0">map</span>(Stream::of)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">   </span>.<span style="color:#4070a0">orElseGet</span>(Stream::empty)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">   </span>.<span style="color:#4070a0">forEach</span>(option<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>option.<span style="color:#4070a0">accept</span>(<span style="color:#007020;font-weight:bold">this</span>))<span style="color:#bbb">
</span></span></span></code></pre></div>
      </div>
    </div><p>And what is this <code>ModelOption</code> class? This is just a synonym for a <code>Consumer&lt;SomeModel&gt;</code>
(so not strictly needed, but can help with readability).
It&rsquo;s a nested interface:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">interface</span> <span style="color:#0e84b5;font-weight:bold">ModelOption</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">extends</span><span style="color:#bbb"> </span>Consumer<span style="color:#666">&lt;</span>SomeModel<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>{}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Next, we create similar utility methods that will update the model instance:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>ModelOption<span style="color:#bbb"> </span><span style="color:#06287e">modelName</span>(String<span style="color:#bbb"> </span>modelName)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">modelName</span><span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>modelName;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>ModelOption<span style="color:#bbb"> </span><span style="color:#06287e">temperature</span>(Float<span style="color:#bbb"> </span>temperature)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">temperature</span><span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>temperature;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span>ModelOption<span style="color:#bbb"> </span><span style="color:#06287e">maxOutputTokens</span>(Integer<span style="color:#bbb"> </span>maxOutputTokens)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">maxOutputTokens</span><span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>maxOutputTokens;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Now, if we want to create a model, we&rsquo;ll be able to call the constructor as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">fn.builder.SomeModel</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import static</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">fn.builder.SomeModel.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">//...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>SomeModel<span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>SomeModel(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>modelName(<span style="color:#4070a0">&#34;gemini&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>temperature(0.<span style="color:#4070a0">5f</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>maxOutputTokens(100)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span></code></pre></div><p>Don&rsquo;t forget to use a <code>static import</code> to keep the syntax short.</p>
<h2 id="discussion">Discussion</h2>
<p>A few advantages I see with this approach:</p>
<ul>
<li>I like the fact we&rsquo;re using a constructor to construct our model instances!</li>
<li>And the constructor is super simple and short!</li>
<li>It also means that the constructor won&rsquo;t change when there&rsquo;s going to be a new parameter to handle (better backward compatibility).
On the other hand, with a traditional builder, the constructor could also take the builder itself as sole parameter.</li>
<li>I&rsquo;m also happy that I got rid of the verbose <code>newBuilder()</code> / <code>build()</code> combo.
It feels like we don&rsquo;t really have a builder at play here.</li>
<li>At first, I was wondering if I was opening the Pandora box, as I feared developers could provide their own lambda
and potentially wreck havoc in my instance construction, but because of visibility rules,
only my methods can modify the internals of the model class</li>
<li>Although we&rsquo;re using a constructor, the fact of passing those method calls as parameters, it feels a bit like having
<a href="https://www.groovy-lang.org/objectorientation.html#_named_parameters">named arguments</a>
like in languages like Python or Groovy (which can also
<a href="https://www.groovy-lang.org/metaprogramming.html#xform-Builder">create builders for you</a> via AST transformations).
It also looks more like the classical builder too, which has that readability aspect.</li>
<li>I can pass the arguments in whichever order I want.</li>
<li>I can put validation rules both in each mutator method and in the constructor after all mutators have been called.</li>
</ul>
<p>Potential tweaks:</p>
<ul>
<li>I used non-final fields, because I wanted to be able to define my default values for some fields at definition time
rather than in the constructor, but we could certainly tweak this implementation a bit if needed.
And anyway, only my mutator methods can alter those fields, so I guess it&rsquo;s fine.</li>
<li>I was curious if I could use Java <code>enum</code>s for storing only my allowed mutators,
but I haven&rsquo;t found an effective and concise way of implementing this.
Java <code>enum</code>s don&rsquo;t work like Rust&rsquo;s, but there&rsquo;s an interesting article about this
<a href="https://www.reddit.com/r/java/comments/135i37c/rust_like_enums_in_java/">here</a> on how to implement sum types.</li>
<li>I wondered also about a mix of <code>sealed</code> <code>interface</code>s and maybe <code>record</code>s, but similarly to <code>enum</code>s,
I couldn&rsquo;t find a nice and short syntax that I was happy with.</li>
</ul>
<p>In the cons:</p>
<ul>
<li>It&rsquo;s a bit unconventional, as I haven&rsquo;t seen this approach implemented in the wild.
So maybe the approach suffers in terms of readability.</li>
<li>The other concerns I have is with discoverability.
When auto-completing code, an IDE like IntelliJ is smart enough to suggest the mutators methods can be used inside the constructor.
But it&rsquo;s not that clear that such mutator methods exist.
It&rsquo;s going to be important to document the constructor to say that those mutators exist.</li>
</ul>
<h2 id="feedback">Feedback</h2>
<p>I&rsquo;d be curious to hear your thoughts on this.
Don&rsquo;t hesitate to interact with me on
<a href="https://uwyn.net/@glaforge/111766219413506355">Mastodon</a>,
<a href="https://twitter.com/glaforge/status/1747272263546905026">Twitter</a>, or
<a href="https://bsky.app/profile/glaforge.bsky.social/post/3kj47jxwseg2m">BlueSky</a></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>URL slug or how to remove accents from strings in Java</title><link>https://glaforge.dev/posts/2024/01/08/url-slug-or-how-to-remove-accents-in-java/</link><pubDate>Mon, 08 Jan 2024 10:02:47 +0100</pubDate><guid>https://glaforge.dev/posts/2024/01/08/url-slug-or-how-to-remove-accents-in-java/</guid><description>&lt;p>In this article, we&amp;rsquo;ll figure out how to create &lt;em>slugs&lt;/em>.
Not the slobbery kind of little gastropods that crawls on the ground. Instead,
we&amp;rsquo;ll see how to create the short hyphened text you can see in the URL of your web browser,
and that is often a &lt;strong>URL-friendly variation of the title of the article&lt;/strong>.&lt;/p>
&lt;blockquote>
&lt;p>Interestingly, one of the most popular posts on my blog is an almost 20 year old article that explains
how to &lt;a href="https://glaforge.dev/posts/2005/04/27/how-to-remove-accents-from-a-string/">remove accents from a string&lt;/a>.
And indeed, in &lt;em>slugs&lt;/em> you would like to remove accents, among other things.&lt;/p></description><content:encoded>
<![CDATA[<p>In this article, we&rsquo;ll figure out how to create <em>slugs</em>.
Not the slobbery kind of little gastropods that crawls on the ground. Instead,
we&rsquo;ll see how to create the short hyphened text you can see in the URL of your web browser,
and that is often a <strong>URL-friendly variation of the title of the article</strong>.</p>
<blockquote>
<p>Interestingly, one of the most popular posts on my blog is an almost 20 year old article that explains
how to <a href="https://glaforge.dev/posts/2005/04/27/how-to-remove-accents-from-a-string/">remove accents from a string</a>.
And indeed, in <em>slugs</em> you would like to remove accents, among other things.</p></blockquote>
<p>So what problem are we trying to solve today?
Let&rsquo;s say you have an article whose title is <em>&ldquo;L&rsquo;été, où est tu ?&rdquo;</em>
(which translates to: <em>&ldquo;Summer, where have you been?&rdquo;</em>&quot;).
You want your blog to have a friendly URL that looks like the title,
but without the punctuation, or the accents (also called diacritical marks),
and you also want to replace spaces with hyphens.
The final URL should then be <code>https://myblog.com/l-ete-ou-est-tu</code>.</p>
<p>A naive approach would be to try to replace all the letters with diacritical marks with their non marked equivalents.
So don&rsquo;t try to replace <em>&ldquo;é&rdquo;</em> with &ldquo;e&rdquo;, etc. You&rsquo;ll likely miss some letters in some languages.
A better approach is to take advantage of <strong>Unicode normalization</strong>.</p>
<p>If you are interested, you can learn more about
<a href="https://unicode.org/reports/tr15/images/UAX15-NormFig4.jpg">unicode normalization</a>
on the <a href="https://unicode.org/reports/tr15/">Unicode.org</a> website.
But in a nutshell, some letters, like accented letters, are a combination of a base letter, and a diacritical mark.</p>
<p>Let&rsquo;s have a look at this image from the link above:</p>
<p><figure>
  <a href="#img-e1eecc79769f255b65984b5a8cc5c0b3">
    <img src="https://unicode.org/reports/tr15/images/UAX15-NormFig3.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-e1eecc79769f255b65984b5a8cc5c0b3">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://unicode.org/reports/tr15/images/UAX15-NormFig3.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>The uppercase <a href="https://www.compart.com/en/unicode/U+212B">angström</a> letter is comprised of
the <em>A uppercase letter</em>, and the <em>ring above</em> diacritical mark.
The composed letter has a unicode value of <code>U+212B</code> but can be decomposed
into <code>U+0041</code> <em>(uppercase A)</em> and <code>U+30A</code> <em>(ring above)</em>.</p>
<p>I&rsquo;ll spare you from the details of the various normalization forms.
But Java allows you to work with the normalized forms of letters thanks to the <code>java.text.Normalizer</code> class.
We&rsquo;ll also take advantage of Java&rsquo;s regex <code>Pattern</code> class to identify particular classes of characters.</p>
<blockquote>
<p>Be sure to check the Javadocs of the <code>Normalizer</code> and <code>Pattern</code> classes:</p>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/text/Normalizer.html">Normalizer</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/regex/Pattern.html">Pattern</a></li>
</ul>
<p>The former explains how to do string normalization, and the latter will give you the list of available character classes.</p></blockquote>
<p>Let&rsquo;s have a look at the following Java snippet:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.text.Normalizer</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>title<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;L&#39;été, où es tu ?&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Normalizer.<span style="color:#4070a0">normalize</span>(title,<span style="color:#bbb"> </span>Normalizer.<span style="color:#4070a0">Form</span>.<span style="color:#4070a0">NFD</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>.<span style="color:#4070a0">toLowerCase</span>()<span style="color:#bbb">                  </span><span style="color:#60a0b0;font-style:italic">// &#34;l&#39;été, où es tu ?&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>.<span style="color:#4070a0">replaceAll</span>(<span style="color:#4070a0">&#34;\\p{IsM}+&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#34;</span>)<span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// &#34;l&#39;ete, ou es tu ?&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">		  </span>.<span style="color:#4070a0">replaceAll</span>(<span style="color:#4070a0">&#34;\\p{IsP}+&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34; &#34;</span>)<span style="color:#bbb">   </span><span style="color:#60a0b0;font-style:italic">// &#34;l ete  ou es tu  &#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>.<span style="color:#4070a0">trim</span>()<span style="color:#bbb">                         </span><span style="color:#60a0b0;font-style:italic">// &#34;l ete  ou es tu&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>.<span style="color:#4070a0">replaceAll</span>(<span style="color:#4070a0">&#34;\\s+&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;-&#34;</span>)<span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic">// &#34;l-ete-ou-es-tu&#34;</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>My approach is usually the following:</p>
<ul>
<li>First, I normalize the text into the <code>NFD</code> form <em>(canonical decomposition)</em>, so base characters and diacritical marks are now separated,</li>
<li>Then, I&rsquo;m replacing all the uppercase letters with lowercase ones,</li>
<li>Next, we use the <code>IsM</code> property which selects the the diacritical marks, and we remove them</li>
<li>Simiarly, we look at the characters which are punctuation, with the <code>IsP</code> binary property, and replace them with spaces</li>
<li>I usually trim the string at that point, as I don&rsquo;t want to have spaces at the beginning or end of the strings (when a punctuation mark is replace with a space in the previous step)</li>
<li>Eventually, all the space characters are replaced with hyphens.</li>
</ul>
<h2 id="slugify">Slugify</h2>
<p>Recently, I came across a Java library that takes care of creating slugs: <a href="https://github.com/slugify/slugify">Slugify</a>!</p>
<p>With Slugify, you can do a similar transformation as mine, with the following code:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.github.slugify.Slugify</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Slugify<span style="color:#bbb"> </span>slugify<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Slugify.<span style="color:#4070a0">builder</span>().<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>slugify.<span style="color:#4070a0">slugify</span>(<span style="color:#4070a0">&#34;L&#39;été, où es tu ?&#34;</span>)<span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">// &#34;lete-ou-es-tu&#34;</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>A small difference here is that the quote is removed, which leads to having the string <code>lete</code> instead of <code>l-ete</code>.
I find that a bit less readable at a glance, but Slugify has various knobs you can tweak to customize its output.</p>
<p>There&rsquo;s one particular thing I like about this library, it&rsquo;s its use of the
<a href="https://unicode-org.github.io/icu/userguide/icu4j/">ICU4J</a> library, which supports <strong>transliteration</strong>
<em>(<a href="https://icu.unicode.org/">ICU</a> is a well known set of libraries for full unicode and globalization support.)</em></p>
<p>The problem with our examples above is that they work well for language with latin-like alphabets.
But my examples keep characters like ideograms intact, and Slugify removes them by default.
If you want to have URLs that stay within the ASCII realm,
you can use <a href="https://en.wikipedia.org/wiki/Transliteration">transliteration</a>,
which can map text in one language into readable latin-like text that sounds like the original text.</p>
<p>So if I wanted to transliterate my string into ascii-friendly text, I could use Slugify&rsquo;s integration of ICU:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.github.slugify.Slugify</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Slugify<span style="color:#bbb"> </span>slugify<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Slugify.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">transliterator</span>(<span style="color:#007020;font-weight:bold">true</span>)<span style="color:#bbb">          </span><span style="color:#60a0b0;font-style:italic">// use transliteration</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">locale</span>(Locale.<span style="color:#4070a0">ENGLISH</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>slugify.<span style="color:#4070a0">slugify</span>(<span style="color:#4070a0">&#34;夏よ、どこにいるの？&#34;</span>)<span style="color:#bbb">  </span><span style="color:#60a0b0;font-style:italic">// &#34;xiayo-dokoniiruno&#34;</span><span style="color:#bbb">
</span></span></span></code></pre></div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Gemini Function Calling</title><link>https://glaforge.dev/posts/2023/12/22/gemini-function-calling/</link><pubDate>Fri, 22 Dec 2023 18:03:43 +0100</pubDate><guid>https://glaforge.dev/posts/2023/12/22/gemini-function-calling/</guid><description>&lt;p>A promising feature of the Gemini large language model released recently by &lt;a href="https://deepmind.google/">Google DeepMind&lt;/a>,
is the support for &lt;a href="https://ai.google.dev/docs/function_calling">function calls&lt;/a>.
It&amp;rsquo;s a way to supplement the model, by letting it know an external functions or APIs can be called.
So you&amp;rsquo;re not limited by the knowledge cut-off of the model: instead, in the flow of the conversation with the model,
you can pass a list of functions the model will know are available to get the information it needs,
to complete the generation of its answer.&lt;/p></description><content:encoded>
<![CDATA[<p>A promising feature of the Gemini large language model released recently by <a href="https://deepmind.google/">Google DeepMind</a>,
is the support for <a href="https://ai.google.dev/docs/function_calling">function calls</a>.
It&rsquo;s a way to supplement the model, by letting it know an external functions or APIs can be called.
So you&rsquo;re not limited by the knowledge cut-off of the model: instead, in the flow of the conversation with the model,
you can pass a list of functions the model will know are available to get the information it needs,
to complete the generation of its answer.</p>
<p>For example, if you want to ask the model about the weather, it doesn&rsquo;t have the realtime information about the weather forecast.
But we can tell it that there&rsquo;s a function that can be called, to get the forecast for a given location.
Internally, the model will acknowledge it doesn&rsquo;t know the answer about the weather,
but it will request that you call an external function that you describe, using a specific set of parameters which correspond to the user&rsquo;s request.</p>
<p>Just days ago, I wrote about how to <a href="https://glaforge.dev/posts/2023/12/13/get-started-with-gemini-in-java/">get started with Gemini in Java</a>.
In that article, we explored how to use the hand-written Java SDK that is available to interact with Gemini from Java.
However, the Java SDK doesn&rsquo;t yet expose all the features of the model: in particular, function calling is missing.
But not all hope is lost! Because under the hood, the SDK relies on the generated protobuf classes library, which exposes everything!</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>Soon, Gemini will be supported by <a href="https://github.com/langchain4j/langchain4j">LangChain4j</a>,
and the Java SDK will also provide an easier way to take care of function calling.
But in this article, I wanted to explore the use of the internal protobuf classes, to see how to best implement its support in the SDK.</p>
      </div>
    </div><p>Let&rsquo;s go step by step!</p>
<p>Instead of using the <code>GenerativeModel</code> API from the SDK, we&rsquo;ll go straight with the <code>PredictionServiceClient</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(VertexAI<span style="color:#bbb"> </span>vertexAI<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>VertexAI(projectId,<span style="color:#bbb"> </span>location))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>PredictionServiceClient<span style="color:#bbb"> </span>client<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>vertexAI.<span style="color:#4070a0">getPredictionServiceClient</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>...<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>We need to prepare a function declaration to describe the kind of functions that the LLM can ask us to call, and we&rsquo;ll wrap it in a <code>Tool</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>FunctionDeclaration<span style="color:#bbb"> </span>functionDeclaration<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>FunctionDeclaration.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">setName</span>(<span style="color:#4070a0">&#34;getCurrentWeather&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">setDescription</span>(<span style="color:#4070a0">&#34;Get the current weather in a given location&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">setParameters</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Schema.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">setType</span>(Type.<span style="color:#4070a0">OBJECT</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">putProperties</span>(<span style="color:#4070a0">&#34;location&#34;</span>,<span style="color:#bbb"> </span>Schema.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">setType</span>(Type.<span style="color:#4070a0">STRING</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">setDescription</span>(<span style="color:#4070a0">&#34;location&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">build</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">addRequired</span>(<span style="color:#4070a0">&#34;location&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">build</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Tool<span style="color:#bbb"> </span>tool<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Tool.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">addFunctionDeclarations</span>(functionDeclaration)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Functions are described using classes that represent a subset of the OpenAPI 3 specification.</p>

    <div class="admonition important">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>
        <span>Important</span>
      </div>
      <div class="admonition-content">
        <p>This is important to provide descriptions for the functions and its parameters,
as the LLM will use that information to figure out which function to call, and which parameters should be passed.</p>
      </div>
    </div><p>Next, let&rsquo;s prepare a question asking about the weather in Paris, and configuring the text generation request with that prompt and the tool defined above:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>String<span style="color:#bbb"> </span>resourceName<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>String.<span style="color:#4070a0">format</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#4070a0">&#34;projects/%s/locations/%s/publishers/google/models/%s&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>vertexAI.<span style="color:#4070a0">getProjectId</span>(),<span style="color:#bbb"> </span>vertexAI.<span style="color:#4070a0">getLocation</span>(),<span style="color:#bbb"> </span>modelName);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Content<span style="color:#bbb"> </span>questionContent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>ContentMaker.<span style="color:#4070a0">fromString</span>(<span style="color:#4070a0">&#34;What&#39;s the weather in Paris?&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>GenerateContentRequest<span style="color:#bbb"> </span>questionContentRequest<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>GenerateContentRequest.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setEndpoint</span>(resourceName)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setModel</span>(resourceName)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">addTools</span>(tool)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">addContents</span>(questionContent)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>ResponseStream<span style="color:#666">&lt;</span>GenerateContentResponse<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>responseStream<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ResponseStream<span style="color:#666">&lt;&gt;</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ResponseStreamIteratorWithHistory<span style="color:#666">&lt;&gt;</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>client<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">streamGenerateContentCallable</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">call</span>(questionContentRequest)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">iterator</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>GenerateContentResponse<span style="color:#bbb"> </span>generateContentResponse<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>responseStream.<span style="color:#4070a0">stream</span>().<span style="color:#4070a0">findFirst</span>().<span style="color:#4070a0">get</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Content<span style="color:#bbb"> </span>callResponseContent<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>generateContentResponse.<span style="color:#4070a0">getCandidates</span>(0).<span style="color:#4070a0">getContent</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>If you print the <code>callResponseContent</code> variable, you&rsquo;ll see that it contains a function call request,
suggesting that you should call the predefined function with the parameter of <code>Paris</code>:</p>
<pre tabindex="0"><code>role: &#34;model&#34;
parts {
  function_call {
    name: &#34;getCurrentWeather&#34;
    args {
      fields {
        key: &#34;location&#34;
        value {
          string_value: &#34;Paris&#34;
        }
      }
    }
  }
}
</code></pre><p>At that point, as the developer, it&rsquo;s your turn to work a little, and make the call to that function yourself!
Let&rsquo;s pretend I called an external Web Service that gives weather information, and that it returns some JSON payload that would look like so:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;weather&#34;</span>: <span style="color:#4070a0">&#34;sunny&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;location&#34;</span>: <span style="color:#4070a0">&#34;Paris&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>We need now to create a function response structure to pass that information back to the LLM:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Content<span style="color:#bbb"> </span>contentFnResp<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Content.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">addParts</span>(Part.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">setFunctionResponse</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>FunctionResponse.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">setResponse</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>Struct.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span>.<span style="color:#4070a0">putFields</span>(<span style="color:#4070a0">&#34;weather&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>Value.<span style="color:#4070a0">newBuilder</span>().<span style="color:#4070a0">setStringValue</span>(<span style="color:#4070a0">&#34;sunny&#34;</span>).<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span>.<span style="color:#4070a0">putFields</span>(<span style="color:#4070a0">&#34;location&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                            </span>Value.<span style="color:#4070a0">newBuilder</span>().<span style="color:#4070a0">setStringValue</span>(<span style="color:#4070a0">&#34;Paris&#34;</span>).<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span>.<span style="color:#4070a0">build</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">build</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">build</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>Then, since LLMs are actually stateless beasts, we need to give it the whole context of the conversation again,
passing the query, the function call response the model suggested us to make, as well as the response we got from the external weather service:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>GenerateContentRequest<span style="color:#bbb"> </span>generateContentRequest<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>GenerateContentRequest.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">setEndpoint</span>(resourceName)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">setModel</span>(resourceName)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">addContents</span>(questionContent)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">addContents</span>(callResponseContent)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">addContents</span>(contentFnResp)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">addTools</span>(tool)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>And to finish, we&rsquo;ll invoke the <code>client</code> one last time with that whole dialog and information, and print a response out:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>responseStream<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ResponseStream<span style="color:#666">&lt;&gt;</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ResponseStreamIteratorWithHistory<span style="color:#666">&lt;&gt;</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>client<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">streamGenerateContentCallable</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">call</span>(generateContentRequest)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>.<span style="color:#4070a0">iterator</span>())<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(GenerateContentResponse<span style="color:#bbb"> </span>resp<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>responseStream)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(ResponseHandler.<span style="color:#4070a0">getText</span>(resp));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>And happily, Gemini will reply to us that:</p>
<pre tabindex="0"><code>The weather in Paris is sunny.
</code></pre><p>What a lovely way to start the holiday season with a nice and sunny weather!</p>
<p>I wish you all happy year end festivities, and I look forward to seeing you next year.
Hopefully next month, I&rsquo;ll be able to show you some cool new SDK features or the LangChain4j integration!
Thanks for reading.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Visualize and Inspect Workflows Executions</title><link>https://glaforge.dev/posts/2023/12/22/visualize-and-inspect-workflows-executions/</link><pubDate>Fri, 22 Dec 2023 12:02:42 +0100</pubDate><guid>https://glaforge.dev/posts/2023/12/22/visualize-and-inspect-workflows-executions/</guid><description>&lt;p>When using a service like Google Cloud &lt;a href="https://cloud.google.com/workflows/">Workflows&lt;/a>,
in particular as your workflows get bigger, it can be difficult to understand what&amp;rsquo;s going on under the hood.
With multiple branches, step jumps, iterations, and also parallel branches and iterations,
if your workflow fails during an execution, until now, you had to check the execution status,
or go deep through the logs to find more details about the failed step.&lt;/p>
&lt;p>I have good news for you!
Workflows recently added some deeper introspection capability:
you can now &lt;a href="https://cloud.google.com/workflows/docs/debug-steps">view the history of execution steps&lt;/a>.
From the Google Cloud console, you can see the lists of steps, and see the logical flow between them.
The usual workflow visualisation will also highlight in green the successful steps, and in red the failed one.
Of course, it is also possible to make a curl call to get the JSON of the
&lt;a href="https://cloud.google.com/workflows/docs/debug-steps#list-entries">list of executed steps&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>When using a service like Google Cloud <a href="https://cloud.google.com/workflows/">Workflows</a>,
in particular as your workflows get bigger, it can be difficult to understand what&rsquo;s going on under the hood.
With multiple branches, step jumps, iterations, and also parallel branches and iterations,
if your workflow fails during an execution, until now, you had to check the execution status,
or go deep through the logs to find more details about the failed step.</p>
<p>I have good news for you!
Workflows recently added some deeper introspection capability:
you can now <a href="https://cloud.google.com/workflows/docs/debug-steps">view the history of execution steps</a>.
From the Google Cloud console, you can see the lists of steps, and see the logical flow between them.
The usual workflow visualisation will also highlight in green the successful steps, and in red the failed one.
Of course, it is also possible to make a curl call to get the JSON of the
<a href="https://cloud.google.com/workflows/docs/debug-steps#list-entries">list of executed steps</a>.</p>
<p>Let&rsquo;s have a look!</p>
<p>In the console, when you click on an execution, in the <code>summary</code> tab,
you&rsquo;ll see not only the failed step, but also the nice workflow graph colored green and red:</p>
<p><figure>
  <a href="#img-30d115c7a052d147848b62eccc201eb7">
    <img src="/img/workflows-days/step-visu-1.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-30d115c7a052d147848b62eccc201eb7">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-days/step-visu-1.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>That way, you know which path the execution followed, in a visual manner.
But you can also see the actual list of steps executed, with more details, by clicking on the <code>steps</code> tab:</p>
<p><figure>
  <a href="#img-31d2be5e47264a042a3a34ef273052fe">
    <img src="/img/workflows-days/step-visu-2.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-31d2be5e47264a042a3a34ef273052fe">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-days/step-visu-2.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>From this table, the filter will let you further refine particular type of steps you&rsquo;d like to investigate,
or visualise the steps of a subworkflow only, etc.</p>
<p>This is a nice improvement to the developer experience, and for your ops team,
to better understand what happens during your workflow executions!
Feel free to read more about this new capabability in the documentation about
<a href="https://cloud.google.com/workflows/docs/debug-steps">viewing the history of execution steps</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Hands on Codelabs to dabble with Large Language Models in Java</title><link>https://glaforge.dev/posts/2023/12/18/get-hands-on-codelabs-to-dabble-with-llms/</link><pubDate>Mon, 18 Dec 2023 17:31:56 +0100</pubDate><guid>https://glaforge.dev/posts/2023/12/18/get-hands-on-codelabs-to-dabble-with-llms/</guid><description>&lt;p>Hot on the heels of the &lt;a href="https://glaforge.dev/posts/2023/12/13/get-started-with-gemini-in-java/">release of Gemini&lt;/a>,
I&amp;rsquo;d like to share a couple of resources I created to get your hands on large language models,
using &lt;a href="https://github.com/langchain4j/">LangChain4J&lt;/a>, and the &lt;a href="https://ai.google/discover/palm2/">PaLM 2&lt;/a> model.
Later on, I&amp;rsquo;ll also share with you articles and codelabs that take advantage of Gemini, of course.&lt;/p>
&lt;p>The PaLM 2 model supports 2 modes:&lt;/p>
&lt;ul>
&lt;li>text generation,&lt;/li>
&lt;li>and chat.&lt;/li>
&lt;/ul>
&lt;p>In the 2 codelabs, you&amp;rsquo;ll need to have created an account on Google Cloud, and created a project.
The codelabs will guide you through the steps to setup the environment,
and show you how to use the Google Cloud built-in shell and code editor, to develop in the cloud.&lt;/p></description><content:encoded>
<![CDATA[<p>Hot on the heels of the <a href="https://glaforge.dev/posts/2023/12/13/get-started-with-gemini-in-java/">release of Gemini</a>,
I&rsquo;d like to share a couple of resources I created to get your hands on large language models,
using <a href="https://github.com/langchain4j/">LangChain4J</a>, and the <a href="https://ai.google/discover/palm2/">PaLM 2</a> model.
Later on, I&rsquo;ll also share with you articles and codelabs that take advantage of Gemini, of course.</p>
<p>The PaLM 2 model supports 2 modes:</p>
<ul>
<li>text generation,</li>
<li>and chat.</li>
</ul>
<p>In the 2 codelabs, you&rsquo;ll need to have created an account on Google Cloud, and created a project.
The codelabs will guide you through the steps to setup the environment,
and show you how to use the Google Cloud built-in shell and code editor, to develop in the cloud.</p>
<p>You should be a Java developer, as the examples are in Java, use the <a href="https://github.com/langchain4j/">LangChain4J</a> project, and Maven for building the code.</p>
<h3 id="generative-ai-text-generation-in-java-with-palm-and-langchain4jhttpscodelabsdevelopersgooglecomcodelabsgenai-text-gen-java-palm-langchain4jhlen0"><a href="https://codelabs.developers.google.com/codelabs/genai-text-gen-java-palm-langchain4j?hl=en#0">Generative AI text generation in Java with PaLM and LangChain4J</a></h3>
<p>In the first <a href="https://codelabs.developers.google.com/codelabs/genai-text-gen-java-palm-langchain4j?hl=en#0">codelab</a>
you can explore:</p>
<ul>
<li>how to make your first call to PaLM for simple question/answer scenarios</li>
<li>how to extract structured data out of unstructured text</li>
<li>how to use prompts and prompt templates</li>
<li>how to classify text, with an example on sentiment analysis</li>
</ul>
<h3 id="generative-ai-powered-chat-with-users-and-docs-in-java-with-palm-and-langchain4jhttpscodelabsdevelopersgooglecomcodelabsgenai-chat-java-palm-langchain4jhlen0"><a href="https://codelabs.developers.google.com/codelabs/genai-chat-java-palm-langchain4j?hl=en#0">Generative AI powered chat with users and docs in Java with PaLM and LangChain4J</a></h3>
<p>In the second <a href="https://codelabs.developers.google.com/codelabs/genai-chat-java-palm-langchain4j?hl=en#0">codelab</a>
you&rsquo;ll use the chat model to learn:</p>
<ul>
<li>how to create your first chat with the PaLM model</li>
<li>how to give your chatbot a personality, with an example with a chess player</li>
<li>how to extract structured data out of unstructured text using LangChain4J&rsquo;s AiServices and its annotations</li>
<li>how to implement Retrieval Augmented Generation (RAG) to answer questions about your own documentation</li>
</ul>
<h2 id="going-further-with-generative-ai">Going further with Generative AI</h2>
<p>If you&rsquo;re interested in going further with Generative AI, and learn more,
feel free to <a href="https://goo.gle/generativeai">join the Google Cloud Innovators program</a>.</p>
<p>Google Cloud Innovators is <strong>free</strong> and includes:</p>
<ul>
<li>live discussions, AMAs, and roadmap sessions to learn the latest directly from Googlers,</li>
<li>the latest Google Cloud news right in your inbox,</li>
<li>digital badge and video conference background,</li>
<li>and more.</li>
</ul>
<p>Go check what the <a href="https://cloud.google.com/innovators?hl=en">program offers</a>!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Get Started with Gemini in Java</title><link>https://glaforge.dev/posts/2023/12/13/get-started-with-gemini-in-java/</link><pubDate>Wed, 13 Dec 2023 16:45:51 +0100</pubDate><guid>https://glaforge.dev/posts/2023/12/13/get-started-with-gemini-in-java/</guid><description>&lt;p>Google announced today the availability of
&lt;a href="https://cloud.google.com/blog/products/ai-machine-learning/gemini-support-on-vertex-ai">Gemini&lt;/a>,
its latest and more powerful Large Language Model.
Gemini is &lt;strong>multimodal&lt;/strong>, which means it&amp;rsquo;s able to consume not only text, but also images or videos.&lt;/p>
&lt;p>I had the pleasure of working on the Java samples and help with the Java SDK, with wonderful engineer colleagues, and I&amp;rsquo;d like to share some examples of &lt;strong>what you can do with Gemini, using Java&lt;/strong>!&lt;/p>
&lt;p>First of all, you&amp;rsquo;ll need to have an account on Google Cloud and created a project.
The Vertex AI API should be enabled, to be able to access the Generative AI services,
and in particular the Gemini large language model.
Be sure to check out the
&lt;a href="https://cloud.google.com/vertex-ai/docs/generative-ai/start/quickstarts/quickstart-multimodal?hl=en">instructions&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>Google announced today the availability of
<a href="https://cloud.google.com/blog/products/ai-machine-learning/gemini-support-on-vertex-ai">Gemini</a>,
its latest and more powerful Large Language Model.
Gemini is <strong>multimodal</strong>, which means it&rsquo;s able to consume not only text, but also images or videos.</p>
<p>I had the pleasure of working on the Java samples and help with the Java SDK, with wonderful engineer colleagues, and I&rsquo;d like to share some examples of <strong>what you can do with Gemini, using Java</strong>!</p>
<p>First of all, you&rsquo;ll need to have an account on Google Cloud and created a project.
The Vertex AI API should be enabled, to be able to access the Generative AI services,
and in particular the Gemini large language model.
Be sure to check out the
<a href="https://cloud.google.com/vertex-ai/docs/generative-ai/start/quickstarts/quickstart-multimodal?hl=en">instructions</a>.</p>
<h2 id="preparing-your-project-build">Preparing your project build</h2>
<p>To get started with some coding, you&rsquo;ll need to create a Gradle or a Maven build file
that requires the Google Cloud libraries BOM, and the <code>google-cloud-vertexai</code> library.
Here&rsquo;s an example with Maven:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependencyManagement&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;dependencies&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>libraries-bom<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>com.google.cloud<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;scope&gt;</span>import<span style="color:#062873;font-weight:bold">&lt;/scope&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;type&gt;</span>pom<span style="color:#062873;font-weight:bold">&lt;/type&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>26.29.0<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;/dependencies&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependencyManagement&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependencies&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>com.google.cloud<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>google-cloud-vertexai<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span><span style="display:flex;"><span>    ...
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependencies&gt;</span>
</span></span><span style="display:flex;"><span>...
</span></span></code></pre></div><h2 id="your-first-queries">Your first queries</h2>
<p>Now let&rsquo;s have a look at our first multimodal example, mixing text prompts and images:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(VertexAI<span style="color:#bbb"> </span>vertexAI<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>VertexAI(projectId,<span style="color:#bbb"> </span>location))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#902000">byte</span><span style="color:#666">[]</span><span style="color:#bbb"> </span>imageBytes<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Base64.<span style="color:#4070a0">getDecoder</span>().<span style="color:#4070a0">decode</span>(dataImageBase64);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>GenerativeModel<span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GenerativeModel(<span style="color:#4070a0">&#34;gemini-pro-vision&#34;</span>,<span style="color:#bbb"> </span>vertexAI);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>GenerateContentResponse<span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>model.<span style="color:#4070a0">generateContent</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>ContentMaker.<span style="color:#4070a0">fromMultiModalData</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;What is this image about?&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>PartMaker.<span style="color:#4070a0">fromMimeTypeAndData</span>(<span style="color:#4070a0">&#34;image/jpg&#34;</span>,<span style="color:#bbb"> </span>imageBytes)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(ResponseHandler.<span style="color:#4070a0">getText</span>(response));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>You instantiate <code>VertexAI</code> with your Google Cloud project ID, and the region location of your choice.
To pass images to Gemini, you should either pass the bytes directly,
or you can pass a URI of an image stored in a cloud storage bucket (like <code>gs://my-bucket/my-img.jpg</code>).
You create an instance of the model. Here, I&rsquo;m using <code>gemini-pro-vision</code>.
But later on, a <code>gemini-ultra-vision</code> model will also be available.
Let&rsquo;s ask the model to generate content with the <code>generateContent()</code> method,
by passing both a text prompt, and also an image.
The <code>ContentMaker</code> and <code>PartMaker</code> classes are helpers to further simplify
the creation of more advanced prompts that mix different modalities.
But you could also just pass a simple string as argument of the <code>generateContent()</code> method.
The <code>ResponseHandler</code> utility will retrieve all the text of the answer of the model.</p>
<p>Instead of getting the whole output once all the text is generated,
you can also adopt a streaming approach:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>model.<span style="color:#4070a0">generateContentStream</span>(<span style="color:#4070a0">&#34;Why is the sky blue?&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">stream</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">forEach</span>(System.<span style="color:#4070a0">out</span>::print);<span style="color:#bbb">
</span></span></span></code></pre></div><p>You can also iterate over the stream with a <code>for</code> loop:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>ResponseStream<span style="color:#666">&lt;</span>GenerateContentResponse<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>responseStream<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>model.<span style="color:#4070a0">generateContentStream</span>(<span style="color:#4070a0">&#34;Why is the sky blue?&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(GenerateContentResponse<span style="color:#bbb"> </span>responsePart:<span style="color:#bbb"> </span>responseStream)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">print</span>(ResponseHandler.<span style="color:#4070a0">getText</span>(responsePart));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="lets-chat">Let&rsquo;s chat!</h2>
<p>Gemini is a multimodal model, and it&rsquo;s actually both a text generation model, but also a chat model.
So you can chat with Gemini, and ask a series of questions in context.
There&rsquo;s a handy <code>ChatSession</code> utility class which simplifies the handling of the conversation:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(VertexAI<span style="color:#bbb"> </span>vertexAI<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>VertexAI(projectId,<span style="color:#bbb"> </span>location))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>GenerateContentResponse<span style="color:#bbb"> </span>response;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>GenerativeModel<span style="color:#bbb"> </span>model<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GenerativeModel(modelName,<span style="color:#bbb"> </span>vertexAI);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>ChatSession<span style="color:#bbb"> </span>chatSession<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ChatSession(model);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>chatSession.<span style="color:#4070a0">sendMessage</span>(<span style="color:#4070a0">&#34;Hello.&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(ResponseHandler.<span style="color:#4070a0">getText</span>(response));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>chatSession.<span style="color:#4070a0">sendMessage</span>(<span style="color:#4070a0">&#34;What are all the colors in a rainbow?&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(ResponseHandler.<span style="color:#4070a0">getText</span>(response));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>chatSession.<span style="color:#4070a0">sendMessage</span>(<span style="color:#4070a0">&#34;Why does it appear when it rains?&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(ResponseHandler.<span style="color:#4070a0">getText</span>(response));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>This is convenient to use <code>ChatSession</code> as it takes care of keeping track
of past questions from the user, and answers from the assistant.</p>
<h2 id="going-further">Going further</h2>
<p>This is just a few examples of the capabilities of Gemini. Be sure to check out some of the
<a href="https://github.com/GoogleCloudPlatform/java-docs-samples/tree/main/vertexai/snippets/src/main/java/vertexai/gemini">samples that are available on Github</a>.
Read <a href="https://cloud.google.com/vertex-ai/docs/generative-ai/start/quickstarts/quickstart-multimodal?hl=en">more about Gemini and Generative AI</a> in the Google Cloud documentation.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Generative AI in practice: Concrete LLM use cases in Java, with the PaLM API</title><link>https://glaforge.dev/talks/2023/11/13/gen-ai-with-palm-2-and-java/</link><pubDate>Mon, 13 Nov 2023 15:36:51 +0100</pubDate><guid>https://glaforge.dev/talks/2023/11/13/gen-ai-with-palm-2-and-java/</guid><description>&lt;p>Large Language Models, available through easy to use APIs, bring powerful machine learning tools in the hands of developers.
Although Python is usually seen as the &lt;em>lingua franca&lt;/em> of everything ML, with LLM APIs and LLM orchestration frameworks, complex tasks become easier to implement for enterprise developers.&lt;/p>
&lt;h2 id="abstract">Abstract&lt;/h2>
&lt;blockquote>
&lt;p>Large language models (LLMs) are a powerful new technology that can be used for a variety of tasks, including generating text, translating languages, and writing different kinds of creative content. However, LLMs can be difficult to use, especially for developers who are not proficient in Python, the lingua franca for AI. So what about us Java developers? How can we make use of Generative AI?&lt;/p></description><content:encoded>
<![CDATA[<p>Large Language Models, available through easy to use APIs, bring powerful machine learning tools in the hands of developers.
Although Python is usually seen as the <em>lingua franca</em> of everything ML, with LLM APIs and LLM orchestration frameworks, complex tasks become easier to implement for enterprise developers.</p>
<h2 id="abstract">Abstract</h2>
<blockquote>
<p>Large language models (LLMs) are a powerful new technology that can be used for a variety of tasks, including generating text, translating languages, and writing different kinds of creative content. However, LLMs can be difficult to use, especially for developers who are not proficient in Python, the lingua franca for AI. So what about us Java developers? How can we make use of Generative AI?</p>
<p>This presentation will go through how to use LLMs in Java without the need for Python. We will use the PaLM API, provided by Google Cloud’s Vertex AI services, to perform a variety of tasks, such as searching through documentation, generating kids stories, summarizing content, extracting keywords or entities, and more.
In our journey through demos, we&rsquo;ll discover LangChain4J, a wonderful LLM orchetratore for Java developers that simplifies the implementation of advanced LLM use cases.</p></blockquote>
<p>I had the chance to get this talk recorded at Devoxx Belgium:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/ioTPfL9cd9k?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>And you can check the slides here:</p>
<script async class="speakerdeck-embed" data-id="be0c44ac898f4ce5b905a8389bb751e2" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Tech Watch #5 — November, 15, 2023</title><link>https://glaforge.dev/posts/2023/11/13/tech-watch-0005/</link><pubDate>Mon, 13 Nov 2023 12:33:50 +0100</pubDate><guid>https://glaforge.dev/posts/2023/11/13/tech-watch-0005/</guid><description>&lt;ul>
&lt;li>
&lt;p>Some friends shared this article from Uwe Friedrichsen, tilted &lt;a href="https://www.ufried.com/blog/back_to_the_future/">back to the future&lt;/a>, that talks about this feeling of &amp;ldquo;déjà-vu&amp;rdquo;, this impression that in IT we keep on reinventing the wheel. With references to mainframes, Uwe compared CICS to Lambda function scheduling, JCL to step functions, mainframe software development environments to the trendy platform engineering. There are two things I like about this article. First of all, it rings a bell with me, as we&amp;rsquo;ve seen the pendulum swing as we keep reinventing some patterns or rediscovering certain best practices, sometimes favoring an approach one day, and coming back to another approach the next day. But secondly, Uwe referenced Gunter Dueck who talked about spirals rather than a pendulum. I&amp;rsquo;ve had that same analogy in mind for years: rather than swinging on one side to the next and back, I always had this impression that we&amp;rsquo;re circling and spiraling, but each time, even when passing on the same side, we&amp;rsquo;ve learned something along the way, and we&amp;rsquo;re getting closer to an optimum, with a slightly different view angle, and hopefully with a better view and more modern practices. Last week at FooConf #2 in Helsinki, I was just talking with my friend &lt;a href="https://agiledeveloper.com/aboutus.html">Venkat Subramaniam&lt;/a> about this spiral visualisation, and I&amp;rsquo;m glad to see I&amp;rsquo;m not the only one thinking that IT is spiraling rather than swinging like a pendulum.&lt;/p></description><content:encoded>
<![CDATA[<ul>
<li>
<p>Some friends shared this article from Uwe Friedrichsen, tilted <a href="https://www.ufried.com/blog/back_to_the_future/">back to the future</a>, that talks about this feeling of &ldquo;déjà-vu&rdquo;, this impression that in IT we keep on reinventing the wheel. With references to mainframes, Uwe compared CICS to Lambda function scheduling, JCL to step functions, mainframe software development environments to the trendy platform engineering. There are two things I like about this article. First of all, it rings a bell with me, as we&rsquo;ve seen the pendulum swing as we keep reinventing some patterns or rediscovering certain best practices, sometimes favoring an approach one day, and coming back to another approach the next day. But secondly, Uwe referenced Gunter Dueck who talked about spirals rather than a pendulum. I&rsquo;ve had that same analogy in mind for years: rather than swinging on one side to the next and back, I always had this impression that we&rsquo;re circling and spiraling, but each time, even when passing on the same side, we&rsquo;ve learned something along the way, and we&rsquo;re getting closer to an optimum, with a slightly different view angle, and hopefully with a better view and more modern practices. Last week at FooConf #2 in Helsinki, I was just talking with my friend <a href="https://agiledeveloper.com/aboutus.html">Venkat Subramaniam</a> about this spiral visualisation, and I&rsquo;m glad to see I&rsquo;m not the only one thinking that IT is spiraling rather than swinging like a pendulum.</p>
</li>
<li>
<p><a href="https://automerge.org/blog/2023/11/06/automerge-repo/">Automerge-repo, a batteries included toolkit for building local-first applications</a><br />
<a href="https://automerge.org/">Automerge</a> is one of the most well-known CRDT algorithm (Conflict-Free Replicated Data Type) that allows you to implement collaborative applications (think Google Docs kind of collaboration, for example). With CRDT algorithms and data structures, concurrent changes on different devices can be merged automatically without requiring a central server, and without complex merge processes. However, having an algorithm and data structure is one thing, but to put the whole system in place is not necessarily easy. This new automerge-repo projects tries to solve this problem, by offering networking and storage adapters to facilitate the communication between the peers, or with a potential sync server.</p>
</li>
<li>
<p>The WebAssembly Garbage Collection proposal (WasmGC) lands in the latest Chrome version. The <a href="https://v8.dev/blog/wasm-gc-porting">V8 team dives into the details about WasmGC</a>. It&rsquo;ll be particularly useful to better support garbage collected languages (like Java and friends) without having to ship a garbage collector in each wasm package.</p>
</li>
<li>
<p>Although I&rsquo;m not developing native apps for Macs, I spotted this article about an <a href="https://gregoryszorc.com/blog/2022/08/08/achieving-a-completely-open-source-implementation-of-apple-code-signing-and-notarization/">open source implementation of Apple code signing and notarization</a>, implemented in Rust, and that can run on non-Mac hardware. With this approach, when you&rsquo;re building native apps for the Mac, you can integrate that approach in your Linux-based CI/CD pipeline, without having a Mac box somewhere.</p>
</li>
<li>
<p><a href="https://medium.com/google-cloud/langchain-chain-types-large-document-summarization-using-langchain-and-google-cloud-vertex-ai-1650801899f6">Document summarization is an area where large language models excel</a>. There are different approaches to do so when your context window can&rsquo;t fit the whole document to summarize. In this article, different approaches are mentioned: stuffing (when it fits in the context window), Map/Reduce to split the content in sections that can be summarised and a summary of summary can be made, and the more sequential Refine method where we summarize what fits in memory, and then ask to refine that first summary with the details of the following sections, till we run out of content.</p>
</li>
<li>
<p>Large Language Models face two big issues: one is hallucinations and how to mitigate them by grounding answers or finding ways to assess the response&rsquo;s factuality, and the other one is prompt injection, as a malignant attacker can misguide an LLM to do something else than what it was programmed for. The folks at Scott Logic developed a demo based on the idea of ImmersiveLabs&rsquo; <a href="https://prompting.ai.immersivelabs.com/">online playground</a> to experiment with prompt injection and techniques to circumvent them. There&rsquo;s also an <a href="https://blog.scottlogic.com/2023/11/03/spy-logic.html">article</a> that talks about the project, and a <a href="https://blog.scottlogic.com/2023/10/31/mitigating-prompt-injections.md.html">video</a> that shows it all in action.</p>
</li>
<li>
<p>My good friend Ken Kousen dives into <a href="https://kousenit.org/2023/11/06/the-magic-of-ai-services-with-langchain4j/">the magic of AI Services with LangChain4J</a>. He has a nice blog post, and also a great accompanying <a href="https://www.youtube.com/watch?v=Bx2OpE1nj34">video</a> on YouTube where he shows some of the powerful features of LangChain4J, in particular the AI service that allows you to decorate an interface with annotations to interact with your large language model and get plain Java types or objects in return.</p>
</li>
<li>
<p>My colleague Romin Irani also <a href="https://medium.com/google-cloud/integrating-langchain4j-and-palm-2-chat-bison-model-a684cefd67af">integrated LangChain4J and the PaLM 2 chat model</a>, showing how to deploy a Google Cloud Function chatbot.</p>
</li>
<li>
<p>Baeldung also gives in <a href="https://www.baeldung.com/java-langchain-basics">introduction to LangChain4J</a> showing the basics of prompts, models, memory management, retrieval, chains, and agents.</p>
</li>
<li>
<p><a href="https://www.linkedin.com/pulse/langchain4j-using-redis-stephan-janssen-lobpe/">LangChain4J using Redis</a>: Stephan Janssen, the founder of Devoxx, is using <a href="https://github.com/langchain4j">LangChain4J</a> inside the Devoxx CFP and schedule application. In this article on LinkedIn, he explains how he used Redis to store vector embeddings corresponding to the talks of the conference, to search for similar talks.</p>
</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Tech Watch #4 — October, 27, 2023</title><link>https://glaforge.dev/posts/2023/10/27/tech-watch-004/</link><pubDate>Fri, 27 Oct 2023 14:06:59 +0200</pubDate><guid>https://glaforge.dev/posts/2023/10/27/tech-watch-004/</guid><description>&lt;ul>
&lt;li>
&lt;p>The &lt;a href="https://www.stateof.ai/">State of AI report&lt;/a> is pretty interesting to read (even if long!). Among the major sections: research, industry, but also politics, safety, and some predictions. You&amp;rsquo;ll find an executive summary in one slide, on slide #8. &lt;br />
&lt;br />
On #22, &lt;strong>emergent capabilities of LLMs&lt;/strong> is covered and mentions Stanford&amp;rsquo;s research that talks about the importance of more linear and continuous measures as otherwise capabilities sound like they emerge out of the blue. &lt;br />
&lt;br />
On #23, they talk about the &lt;strong>context length of LLMs being the new parameter count&lt;/strong>, as models try to have bigger context windows. &lt;br />
&lt;br />
However, on slide #24, they also talk about researchers who showed that &lt;strong>in long context windows the content provided in the middle is more ignored&lt;/strong> by LLMs compared to content at the beginning or end of the window. &lt;br />
So be sure to &lt;strong>put the important bits first or last&lt;/strong>, but not lost in the middle. &lt;br />
&lt;br />
Slide #26 speaks about &lt;strong>smaller models trained with smaller curated datasets and can rival 50x bigger models&lt;/strong>. &lt;br />
&lt;br />
Slide #28 wonders if we&amp;rsquo;re &lt;strong>running out of human-generated data&lt;/strong>, and thus, if we&amp;rsquo;re going to have our LLMs trained on&amp;hellip; LLM generated data!&lt;/p></description><content:encoded>
<![CDATA[<ul>
<li>
<p>The <a href="https://www.stateof.ai/">State of AI report</a> is pretty interesting to read (even if long!). Among the major sections: research, industry, but also politics, safety, and some predictions. You&rsquo;ll find an executive summary in one slide, on slide #8. <br />
<br />
On #22, <strong>emergent capabilities of LLMs</strong> is covered and mentions Stanford&rsquo;s research that talks about the importance of more linear and continuous measures as otherwise capabilities sound like they emerge out of the blue. <br />
<br />
On #23, they talk about the <strong>context length of LLMs being the new parameter count</strong>, as models try to have bigger context windows. <br />
<br />
However, on slide #24, they also talk about researchers who showed that <strong>in long context windows the content provided in the middle is more ignored</strong> by LLMs compared to content at the beginning or end of the window. <br />
So be sure to <strong>put the important bits first or last</strong>, but not lost in the middle. <br />
<br />
Slide #26 speaks about <strong>smaller models trained with smaller curated datasets and can rival 50x bigger models</strong>. <br />
<br />
Slide #28 wonders if we&rsquo;re <strong>running out of human-generated data</strong>, and thus, if we&rsquo;re going to have our LLMs trained on&hellip; LLM generated data!</p>
</li>
<li>
<p><a href="https://projector.tensorflow.org/">3D visualisation of vector embeddings from Tensorflow</a> <br />
As I&rsquo;m working on a small application that would help visuliase vector embeddings, I was looking for existing apps or articles that show how vectors can be similar, and thus their semantic to be similar as well. And I came across this existing visualisation from the Tensorflow project, which uses the Word2Vec embedding approach. I like the fact you can use different 3D projections techniques like t-SNE or PCA, and you see related vectors closer in the 3D space, as their meaning is closer too.</p>
</li>
<li>
<p><a href="https://www.citusdata.com/blog/2023/10/26/making-postgres-tick-new-features-in-pg-cron/">A cron extension for PostgreSQL</a><br />
pg_cron is an extension for the PostgreSQL database that adds scheduling capabilities. It can even be scheduled to run your procedures or other SQL queries every few seconds.</p>
</li>
<li>
<p><a href="https://protomaps.com/">Protomaps</a> is a free and open source map of the world, deployable as a single static file on cloud storage (including Google Cloud Storage). You can use OpenStreetMap tiles, as it&rsquo;s distributed with a version of OSM. It&rsquo;s using an efficient and open archive format for pyramids of tile data, accessible via HTTP Range requests.</p>
</li>
<li>
<p><a href="https://artistassistapp.com/">ArtistAssistApp</a> is an application which can tell you which oil or water color paints to use and mix to create similar looking colors for your painting, as you try to reproduce a photo. As a wannabe painter myself, I always struggle creating mixes that match real colors, and this tool is pretty clever to let you find the right mix (at least if you use some well-known paint brands). This also reminds me of <a href="https://scrtwpns.com/mixbox/">mixbox</a> which simulates color mixing as real color pigments mix in real paint, and using such algorithm would greatly improve the real-life accuracy of color mixes in digital art painting applications.</p>
</li>
<li>
<p><a href="https://vectorizer.ai/">Vectorizer</a> is an online tool to transform an image into an SVG file. As I&rsquo;m playing a bit with Generative AI-based image generation, sometimes, the upscalers don&rsquo;t suffice, and you want to transform a nice generated image into a vectorial format (for example clipart-like illustrations), so they scale gracefully in slide decks or on websites.</p>
</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Tech Watch #3 — October, 20, 2023</title><link>https://glaforge.dev/posts/2023/10/20/tech-watch-003/</link><pubDate>Fri, 20 Oct 2023 17:32:51 +0200</pubDate><guid>https://glaforge.dev/posts/2023/10/20/tech-watch-003/</guid><description>&lt;ul>
&lt;li>
&lt;p>&lt;a href="https://horstmann.com/unblog/2023-10-03/index.html">Stop Using char in Java. And Code Points&lt;/a>&lt;br />
It&amp;rsquo;s a can of worms, when you start messing with chars, code points, and you&amp;rsquo;re likely going to get it wrong in the end. As much as possible, stay away from chars and code points, and instead, use as much as possible the String methods like &lt;code>indexOf()&lt;/code> / &lt;code>substring()&lt;/code>, and some regex when you really need to find grapheme clusters.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Paul King shared his presentations on &lt;a href="https://speakerdeck.com/paulk/groovy-today">Why use Groovy in 2023&lt;/a> and an
&lt;a href="https://speakerdeck.com/paulk/groovy-roadmap">update on the Groovy 5 roadmap&lt;/a>It&amp;rsquo;s interesting to see how and where Groovy goes beyond what is offered by Java, sometimes thanks to its dynamic nature, sometimes because of its compile-time transformation capabilities. When Groovy adopts the latest Java features, there&amp;rsquo;s always a twist to make things even groovier in Groovy!&lt;/p></description><content:encoded>
<![CDATA[<ul>
<li>
<p><a href="https://horstmann.com/unblog/2023-10-03/index.html">Stop Using char in Java. And Code Points</a><br />
It&rsquo;s a can of worms, when you start messing with chars, code points, and you&rsquo;re likely going to get it wrong in the end. As much as possible, stay away from chars and code points, and instead, use as much as possible the String methods like <code>indexOf()</code> / <code>substring()</code>, and some regex when you really need to find grapheme clusters.</p>
</li>
<li>
<p>Paul King shared his presentations on <a href="https://speakerdeck.com/paulk/groovy-today">Why use Groovy in 2023</a> and an
<a href="https://speakerdeck.com/paulk/groovy-roadmap">update on the Groovy 5 roadmap</a>It&rsquo;s interesting to see how and where Groovy goes beyond what is offered by Java, sometimes thanks to its dynamic nature, sometimes because of its compile-time transformation capabilities. When Groovy adopts the latest Java features, there&rsquo;s always a twist to make things even groovier in Groovy!</p>
</li>
<li>
<p><a href="https://blog.scottlogic.com/2023/10/18/the-state-of-webassembly-2023.html">The State of WebAssembly in 2023</a><br />
I often enjoy the articles from the folks at Scott Logic. This one is about a survey they ran on the topic of WebAssembly. Languages like Rust and JavaScript are seeing increased usage (for targeting wasm). Wasm is used a lot for web app development, but serverless seems to be he second most common use case, as well as for hosting plugin environments. The survey also mentions that threads, garbage collection and the new component model are the features developer are most interested in. For WASI, all the I/O related proposals like HTTP, filesystem support, sockets, are the ones developers want (although WASIX which covered this area received mixed reactions).</p>
</li>
<li>
<p><a href="https://arstechnica.com/information-technology/2023/09/telling-ai-model-to-take-a-deep-breath-causes-math-scores-to-soar-in-study/">Tell your LLM to take a deep breath!</a><br />
We tend to humanize large language models via <a href="https://en.wikipedia.org/wiki/Anthropomorphism">anthropomorphism</a>, as much as we see human faces in anything like with <a href="https://en.wikipedia.org/wiki/Pareidolia">pareildolia</a>, although LLMs are neither sentients nor human. So it&rsquo;s pretty ironic that to get a better result in some logic problem solving, we need to tell the LLM to actually take a deep breath! Are they now able to breathe?</p>
</li>
<li>
<p><a href="https://hackerone.com/reports/2199174">Wannabe security researcher asks Bard for vulnerabilities in cURL</a><br />
Large Language Models can be super creative, that&rsquo;s why we employ them to imagine new stories, create narratives, etc. And it seems wannabe security experts believe that what LLMs say is pure facts, probably what happened to this person that reported that they asked Bard to find a vulnerability in cURL! And Bard indeed managed to be creative enough to craft an hypothetical exploit, even explaining where a possible integer overflow could take place. Unfortunately, the generated exploit text contained many errors (wrong method signature, invented changelog, code that doesn&rsquo;t compile, etc.)</p>
</li>
<li>
<p><a href="https://www.beren.io/2023-03-19-LLMs-confabulate-not-hallucinate/">LLMs confabulate, they don&rsquo;t hallucinate</a><br />
A few times, I&rsquo;ve seen this mention on social networks about the fact we should say that LLM confabulate, instead of hallucinate. Confabulation is usually a brain disorder that makes people confidently tell things that may be true or not, in a convincing fashion (they don&rsquo;t even know it&rsquo;s false or a lie). Hallucination is more of a misinterpretation of the sensory input, like having the impression to see a pink elephant! The article linked above explains the rationale.</p>
</li>
<li>
<p>Greg Kamradt tweets about the <a href="https://twitter.com/GregKamradt/status/1711772496159252981">use cases for multimodal vision+text LLMs</a><br />
You&rsquo;d think that you could just get a model that describes a picture as a text, and then mix that description with other text snippets. But models that really fully understand both images and texts are way more powerful than this. In this tweet, Greg distinguishes different scenarios: description, interpretation, recommendation, convertion, extraction, assistance and evaluatation. For example, we could imagine transforming an architecture diagram into a proper Terraform YAML file, or a UI mockup into a snippet of code that builds that UI for real. You You could show a picture of a dish, and ask for its recipe!</p>
</li>
<li>
<p><a href="https://blog.jetbrains.com/blog/2023/10/16/ai-graphics-at-jetbrains-story/">The Story of AI Graphics at JetBrains</a><br />
I&rsquo;ve always loved generative and procedural art, both for games and indeed for art. I really enjoyed this article which is going through the story of how they are generating their nice splash screens and animations for the JetBrains family of products. Neural networks at play here!</p>
</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Tech Watch #2 — Oct 06, 2023</title><link>https://glaforge.dev/posts/2023/10/06/tech-watch-002/</link><pubDate>Fri, 06 Oct 2023 16:34:35 +0200</pubDate><guid>https://glaforge.dev/posts/2023/10/06/tech-watch-002/</guid><description>&lt;ul>
&lt;li>
&lt;p>&lt;a href="https://ig.ft.com/generative-ai/">Generative AI exists because of the transformer&lt;br />
&lt;/a>I confess I rarely read the Financial Times, but they have a really neat articles with animations on how large language models work, thanks to the transformer neural network architecture, an architecture invented by Google in 2017. They talk about text vector embeddings, how the self-attention makes LLM understand the relationship between words and the surrounding context, and also doesn&amp;rsquo;t forget to mention hallucinations, how &amp;ldquo;grounding&amp;rdquo; and RLHF (Reinforcement Learning with Human Feedback) can help mitigate them to some extent.&lt;/p></description><content:encoded>
<![CDATA[<ul>
<li>
<p><a href="https://ig.ft.com/generative-ai/">Generative AI exists because of the transformer<br />
</a>I confess I rarely read the Financial Times, but they have a really neat articles with animations on how large language models work, thanks to the transformer neural network architecture, an architecture invented by Google in 2017. They talk about text vector embeddings, how the self-attention makes LLM understand the relationship between words and the surrounding context, and also doesn&rsquo;t forget to mention hallucinations, how &ldquo;grounding&rdquo; and RLHF (Reinforcement Learning with Human Feedback) can help mitigate them to some extent.</p>
</li>
<li>
<p><a href="https://www.youtube.com/watch?v=ioTPfL9cd9k&amp;t=7s">Generative AI in practice: Concrete LLM use cases in Java, with the PaLM API</a> (video)<br />
At Devoxx Belgium this week, the biggest theme of the conference was Generative AI and Large Language Models. The audience being mainly Java-focused, there was a very strong interest for Java developers to be able to take advantage of GenAI / LLMs in Java, instead of the ubiquitous Python. And all sessions along those lines were fully packed. The conference featured Microsoft&rsquo;s Java SemanticKernel, the open source LangChain4J project, or Spring&rsquo;s AI experimental module. The link above is the video of my presentation I did on using PaLM API but for the Java developer, using different approaches, and also with <a href="https://github.com/langchain4j/langchain4j">LangChain4J</a>.</p>
</li>
<li>
<p><a href="https://medium.com/google-cloud/what-ai-assistant-for-a-developer-is-all-about-723de644a449">What &ldquo;AI-Assistant for a Developer&rdquo; is all about?</a> and <a href="https://seroter.com/2023/09/28/an-ai-assisted-cloud-its-a-thing-now-and-here-are-six-ways-its-already-made-my-cloud-experience-better/">An AI-assisted cloud? It&rsquo;s a thing now, and here are six ways it&rsquo;s already made my cloud experience better</a> are two articles from my colleagues Romin and Richard about how AI assistants will progressively make us, developers, more productive and stay in the flow.</p>
</li>
<li>
<p><a href="https://www.docker.com/blog/introduction-to-heredocs-in-dockerfiles/">Heredoc notation in Dockerfiles<br />
</a>Did you know you can use the &ldquo;heredoc&rdquo; notation in Dockerfiles, like &laquo;EOF some script commands EOF, to run multiple commands like in a Bash file? It&rsquo;s just like writing RUN commands separated with &amp;&amp;, but in a nice script, and without creating extra layers. I think I&rsquo;ll start using this in my next Dockerfiles!</p>
</li>
<li>
<p><a href="https://tonsky.me/blog/unicode/">The Absolute Minimum Every Software Developer Must Know About Unicode in 2023<br />
</a>UTF-8 is pretty much ubiquitous nowadays, so we don&rsquo;t need to think about which encoding is used, but rather think about how to use UTF-8 properly. Unicode assigns a character (actually a code point) a number. Unicode is big, with more than 1 million code points. UTF-8 is an encoding: a way to store code points in memory, but the article mentions the other UTF encodings as well. But unicode is tricky as a characters with a diacritical mark, an emoji, etc, can be graphemes: the combination of several code points. And there are lots of tricky corner cases that stem from that (like the length of a string isn&rsquo;t trivial to calculate). A particular character (like a vowel with an accent) can be look alike another, but their composition of code points might differ, hence why there are different compositions and decompositions. So it&rsquo;s important to normalize strings when doing comparisons.</p>
</li>
<li>
<p><a href="https://github.com/jqlang/jq/releases">Jq released version 1.7<br />
</a>There&rsquo;s a new release of jq, the super handy command-line tool to explore and massage your JSON payloads! The new version uses decimal number literals to preserve precision. A new pick(stream) method to do projections (to filter just the stuff you&rsquo;re interested in), a debug() method to log some messages through stderr, and and abs() function, and plenty other little refinements and bug fixes.</p>
</li>
<li>
<p><a href="https://buildkite.com/blog/goodbye-integers-hello-uuids">Goodbye integers. Hello UUIDv7!<br />
</a>BuildKite&rsquo;s article delves into their choice of UUIDv7 for primary keys. The initially started with a combination of sequential primary keys for efficient indexing, but with a UUID as a secondary key for external use. But the characteristics of the 7th version of the UUID specification leads them to just use UUIDv7 instead. UUID eliminate the issue of coordination in distributed systems. Their random aspect makes them less guessable. But the drawback can be poor database index locality.</p>
</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Client-side consumption of a rate-limited API in Java</title><link>https://glaforge.dev/posts/2023/10/02/client-side-consumption-of-a-rate-limited-api-in-java/</link><pubDate>Mon, 02 Oct 2023 22:48:39 +0200</pubDate><guid>https://glaforge.dev/posts/2023/10/02/client-side-consumption-of-a-rate-limited-api-in-java/</guid><description>&lt;p>In the literature, you&amp;rsquo;ll easily find information on how to rate-limit your API. I even talked about
&lt;a href="https://speakerdeck.com/glaforge/the-never-ending-rest-api-design-debate-devoxx-france-2016?slide=80">Web API rate limitation&lt;/a>
years ago at a conference, covering the usage of
&lt;a href="https://www.ietf.org/archive/id/draft-ietf-httpapi-ratelimit-headers-07.html">HTTP headers like X-RateLimit-*&lt;/a>.&lt;/p>
&lt;p>Rate limiting is important to help your service cope with too much load, or also to implement a tiered pricing scheme
(the more you pay, the more requests you&amp;rsquo;re allowed to make in a certain amount of time). There are useful libraries like
&lt;a href="https://micronaut-projects.github.io/micronaut-ratelimiter/snapshot/guide/index.html">Resilience4j&lt;/a>
that you can configure for Micronaut web controllers, or &lt;a href="https://www.baeldung.com/spring-bucket4j">Bucket4j&lt;/a>
for your Spring controllers.&lt;/p></description><content:encoded>
<![CDATA[<p>In the literature, you&rsquo;ll easily find information on how to rate-limit your API. I even talked about
<a href="https://speakerdeck.com/glaforge/the-never-ending-rest-api-design-debate-devoxx-france-2016?slide=80">Web API rate limitation</a>
years ago at a conference, covering the usage of
<a href="https://www.ietf.org/archive/id/draft-ietf-httpapi-ratelimit-headers-07.html">HTTP headers like X-RateLimit-*</a>.</p>
<p>Rate limiting is important to help your service cope with too much load, or also to implement a tiered pricing scheme
(the more you pay, the more requests you&rsquo;re allowed to make in a certain amount of time). There are useful libraries like
<a href="https://micronaut-projects.github.io/micronaut-ratelimiter/snapshot/guide/index.html">Resilience4j</a>
that you can configure for Micronaut web controllers, or <a href="https://www.baeldung.com/spring-bucket4j">Bucket4j</a>
for your Spring controllers.</p>
<p>Oddly, what is harder to find is information about how to consume APIs that are rate-limited.
Although there are usually way more consumers of rate-limited APIs, than producers of such APIs!</p>
<p>Today, I&rsquo;d like to talk about this topic: how to consume APIs that are rate-limited.
And since I&rsquo;m a Java developer, I&rsquo;ll focus on Java-based solutions.</p>
<p>The use case which led me to talk about this topic (on Twitter / X in particular,
with a <a href="https://twitter.com/glaforge/status/1705933799635268050">fruitful conversation</a> with my followers)
is actually about a Java API which is an SDK for a Web API that is rate-limited.
I&rsquo;ll briefly cover consuming Web APIs, but will focus more on using the Java API instead.</p>
<h2 id="consuming-web-apis">Consuming Web APIs</h2>
<h3 id="rate-limit-headers">Rate limit headers</h3>
<p>Let&rsquo;s say we are calling a Web API that is rate-limited to 60 requests per minute.
We could call it 60 times in a second without hitting the limit then wait for a minute, or call it once every second.
Usually, a sliding time window is used to check that within that minute, no more than 60 requests have been made.</p>
<p>If the rate-limited API is well behaved and provides X-RateLimit headers, you can check what those headers say.
Taking the explanations from the IETF <a href="https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/">draft</a>:</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Rate limit headers</span>
      </div>
      <div class="admonition-content">
        <ul>
<li><strong>RateLimit-Limit</strong>: containing the requests quota in the time window;</li>
<li><strong>RateLimit-Remaining</strong>: containing the remaining requests quota in the current window;</li>
<li><strong>RateLimit-Reset</strong>: containing the time remaining in the current window, specified in seconds.</li>
</ul>
      </div>
    </div><p>Note that the draft mentions RateLimit-* as headers, but often in the wild, I&rsquo;ve seen those headers always prefixed with &ldquo;X-&rdquo; instead.
And sometimes, some APIs add a hyphen between Rate and Limit! So it&rsquo;s hard to create a general consumer class that could deal with
<a href="https://stackoverflow.com/questions/16022624/examples-of-http-api-rate-limiting-http-response-headers">all cases</a>.</p>
<p>Those headers inform you about the quota, how much is left, and when the quota should be back to its full capacity (if you don&rsquo;t consume more requests).
So you could certainly stage your requests accordingly &mdash; we will talk about how to schedule your requests in Java in the second section.</p>
<p>Another thing to keep in mind is that the quota may be shared among API consumers.
Maybe you have several parallel threads that will call the API and consume the API quota.
So when you see the reset header, maybe the API will have been called by another thread already, leaving you with a smaller amount of requests left in the quota.</p>
<h3 id="exponential-backoff-and-jitter">Exponential backoff and jitter</h3>
<p>The API that triggered my research actually doesn&rsquo;t provide any rate limitation headers.
So another approach is needed. A classical approach is to use an exponential backoff.
It was nicely <a href="https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/">documented</a> on the AWS blog, a while ago.</p>
<p>The idea is that when you face an over quota error, you&rsquo;re going to retry the call after, for example, one second.
And if you&rsquo;re getting another error, you&rsquo;ll wait a little longer, by multiplying the interval by a constant, like doubling.
So at first, on the first error, you wait 1 second before retrying, next 2 seconds, then 4 seconds, etc.
You can use a fractional multiplier, of course.</p>
<p>But as explained in the article, if all clients fail at the same time, they will retry roughly at the same time as well, after one, two, four seconds.
So the idea is to add some randomness, the jitter, to more evenly spread out the retries, to avoid having new bursts of traffic at roughly the same moments.</p>
<p>There&rsquo;s another good article on Baeldung about
<a href="https://www.baeldung.com/resilience4j-backoff-jitter">exponential backoff and jitter using Resilience4J</a> for your API consumers.</p>
<h2 id="consuming-a-java-api">Consuming a Java API</h2>
<p>Back to my use case, the underlying Web API I&rsquo;m using doesn&rsquo;t feature rate limitation headers.
And since there&rsquo;s a Java library that wraps that API anyway, I&rsquo;d rather just use that Java API for convenience.
When a rate limit is hit, the API will throw an exception.
So I can catch that exception, and deal with it, maybe applying the same exponential backoff + jitter strategy.</p>
<p>However, I know the rate limit of the API.
So instead of eagerly calling the API as fast as possible, getting an exception, waiting a bit, and trying again&hellip;
I&rsquo;d rather just call the API at the pace I&rsquo;m allowed to use it.</p>
<p>Let&rsquo;s say I have a hypothetical API that takes a String as argument and returns a String:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">class</span>  <span style="color:#0e84b5;font-weight:bold">RateLimitedApi</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">call</span>(String<span style="color:#bbb"> </span>arg)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>arg.<span style="color:#4070a0">toUpperCase</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><h3 id="sleeping-a-bit">Sleeping a bit&hellip;</h3>
<p>A first, but naive, idea would be to just add some pause after each call:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(<span style="color:#902000">int</span><span style="color:#bbb"> </span>i<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>0;<span style="color:#bbb"> </span>i<span style="color:#bbb"> </span><span style="color:#666">&lt;</span><span style="color:#bbb"> </span>20;<span style="color:#bbb"> </span>i<span style="color:#666">++</span>)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>api.<span style="color:#4070a0">call</span>(<span style="color:#4070a0">&#34;abc&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Thread.<span style="color:#4070a0">sleep</span>(100);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>And instead of making the same call with the same argument, you could iterate over an array or list:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(String<span style="color:#bbb"> </span>s<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>args)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>api.<span style="color:#4070a0">call</span>(s);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Thread.<span style="color:#4070a0">sleep</span>(100);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Well, it works, but the API call takes some time as well, so you may have to adjust the sleep time accordingly, so it&rsquo;s not really ideal.
The call could also be longer than the actual wait time really needed between two invocations.</p>
<h3 id="scheduled-execution">Scheduled execution</h3>
<p>A better approach would be to use Java&rsquo;s scheduled executors, with a few threads, in case of long API execution times that overlap.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(<span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>scheduler<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Executors.<span style="color:#4070a0">newScheduledThreadPool</span>(4))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>scheduledCalls<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>scheduler.<span style="color:#4070a0">scheduleAtFixedRate</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>()<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>api.<span style="color:#4070a0">call</span>(<span style="color:#4070a0">&#34;abc&#34;</span>),<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>0,<span style="color:#bbb"> </span>100,<span style="color:#bbb"> </span>TimeUnit.<span style="color:#4070a0">MILLISECONDS</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Instead of calling the API with the same argument, how would you call it for a series of different values,
but then stop the scheduler once we&rsquo;re done with all the values?
You could take advantage of some kind of queue (here a ConcurrentLinkedDeque) to pop the arguments one at a time.
Once you&rsquo;ve cleared all the elements of the queue, you shut down the scheduler altogether.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>args<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>ConcurrentLinkedDeque<span style="color:#666">&lt;&gt;</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>List.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;a&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;b&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;c&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;d&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;e&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;f&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;g&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;h&#34;</span>,<span style="color:#bbb"> </span>...<span style="color:#4070a0">&#34;x&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;y&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;z&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(<span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>scheduler<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Executors.<span style="color:#4070a0">newScheduledThreadPool</span>(4))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>scheduler.<span style="color:#4070a0">scheduleAtFixedRate</span>(()<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(<span style="color:#666">!</span>args.<span style="color:#4070a0">isEmpty</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>api.<span style="color:#4070a0">call</span>(args.<span style="color:#4070a0">pop</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>scheduler.<span style="color:#4070a0">shutdown</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>},<span style="color:#bbb"> </span>0,<span style="color:#bbb"> </span>100,<span style="color:#bbb"> </span>TimeUnit.<span style="color:#4070a0">MILLISECONDS</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><h3 id="one-more-in-the-bucket4j">One more in the Bucket4J!</h3>
<p>In the introduction, I mentioned some great libraries like Resilience4J and Bucket4J.
Let&rsquo;s have a look at an approach using <a href="https://bucket4j.com/">Bucket4J</a>.</p>
<p>Scheduling is fine, to respect the rate limit, but you may perhaps want to get as many calls through as possible,
while still respecting the rate. So a different approach is necessary.</p>
<p>Bucket4J is based on the <a href="https://en.wikipedia.org/wiki/Token_bucket">token bucket algorithm</a>.
It offers a very rich and fine-grained set of rate limit definitions, if you want to allow bursts, or prefer a regular flow (like our schedule earlier).
Be sure to check out the <a href="https://bucket4j.com/8.4.0/toc.html#quick-start-examples">documentation</a> for the details.</p>
<p>Let&rsquo;s see how to define my limited consumption rate of 10 per second:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>args<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>List.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;a&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;b&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;c&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;d&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;e&#34;</span>,<span style="color:#bbb"> </span>...<span style="color:#4070a0">&#34;x&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;y&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;z&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>bucket<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Bucket.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">addLimit</span>(Bandwidth.<span style="color:#4070a0">simple</span>(10,<span style="color:#bbb"> </span>Duration.<span style="color:#4070a0">ofSeconds</span>(1)))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(String<span style="color:#bbb"> </span>arg<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>args)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>bucket.<span style="color:#4070a0">asBlocking</span>().<span style="color:#4070a0">consumeUninterruptibly</span>(1);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>api.<span style="color:#4070a0">call</span>(arg);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>It&rsquo;s pretty explicit: I create a limit that corresponds to a bandwidth of 10 tokens per second.
With this simple strategy, the bucket is refilled greedily: every 100ms a new token will be available again.
But it&rsquo;s also possible to configure it differently, to say that you want to allow another 10 calls once every second.</p>
<p>Then I have a simple for loop to iterate over the list of arguments I must pass to the API,
but I introduce an instruction that blocks until a token is available &mdash; ie. that I have the right to call the API again while respecting the rate limit.</p>
<p>Also beware of API calls that take a lot of time, as here we&rsquo;re using a blocking call that blocks the calling thread.
So if API calls take longer than the time a new token is available in the bucket, you&rsquo;ll end up calling the API much less frequently than the allowed rate limit.</p>
<p>However, with Bucket4J, the bucket can be used in a thread-safe manner, you can have several threads consuming from the same API,
using the same shared bucket, or you can make parallel calls with a single consumer as well, to use the quota to its maximum.</p>
<p>Let&rsquo;s use executors to parallelize our calls:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(ExecutorService<span style="color:#bbb"> </span>executor<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Executors.<span style="color:#4070a0">newFixedThreadPool</span>(4))<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(String<span style="color:#bbb"> </span>arg<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>args)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>bucket.<span style="color:#4070a0">asBlocking</span>().<span style="color:#4070a0">consumeUninterruptibly</span>(1);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>executor.<span style="color:#4070a0">submit</span>(()<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>api.<span style="color:#4070a0">call</span>(arg));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Be careful though, that doing so, your API calls are not necessarily following the exact same order as the order of the input collection.
In my case, I didn&rsquo;t care about the order of execution.</p>
<p>Last little tweak we could make since Java 21 was released recently, we could make use of virtual threads,
instead of threads! So let&rsquo;s push our example forward in the 21th century with this small change when creating our executor service:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Executors.<span style="color:#4070a0">newVirtualThreadPerTaskExecutor</span>()<span style="color:#bbb">
</span></span></span></code></pre></div><p>So far, we have only called the API without taking care of the returned result.
We could update the examples above with an extra line to add the argument and result in a ConcurrentHashMap or to use the result immediately.
Or we could also explore one last solution, using CompletableFutures and/or ExecutorCompletionService.
But I&rsquo;m not 100% satisfied with what I came up with so far.
So I might update this article if I find a convenient and elegant solution later on.</p>
<p>Time to wrap up!</p>
<h2 id="summary">Summary</h2>
<p>In this article, we explored the less covered topic of consuming a rate-limited API.
First, we discussed approaches for consuming Web APIs that are well-behaved, exposing rate limitation headers,
and those less well-behaved using an exponential backoff and jitter approach.
We then moved on to the case of Java APIs, doing a simple sleep to call the API at a cadence that respects the rate limit.
We also had a look at scheduled executions.
And we finished our journey with the help of the powerful Bucket4J library.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Tech Watch #1 — Sept 29, 2023</title><link>https://glaforge.dev/posts/2023/09/29/tech-watch-001/</link><pubDate>Fri, 29 Sep 2023 16:05:13 +0200</pubDate><guid>https://glaforge.dev/posts/2023/09/29/tech-watch-001/</guid><description>&lt;p>Inspired my by super boss &lt;a href="https://twitter.com/rseroter">Richard Seroter&lt;/a> with his regular &lt;a href="https://seroter.com/2023/09/28/daily-reading-list-september-28-2023-171/">daily reading list&lt;/a>, I decided to record and share my &lt;em>tech watch&lt;/em>, every week (or so).
I always take notes of interesting articles I read for my own curiosity and to remember them when I need those references later on. But also to share them with &lt;a href="https://lescastcodeurs.com/">Les Cast Codeurs podcast&lt;/a>!
So I hope it&amp;rsquo;ll be interesting to my readers too!&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;a href="https://www.honeycomb.io/llms-demand-observability-driven-development">LLMs Demand Observability-Driven Development&lt;br />
&lt;/a>A great tribune from &lt;a href="https://twitter.com/mipsytipsy">Charity Majors&lt;/a> on the importance of observability-driven development, in the wake of large language models. Developing LLM based solutions is typically not something you can do with a classical test-driven approach, as you only really get proper test data when you have it coming from production usage. Furthermore, LLMs are pretty much unpredictable and underterministic. But with observability in place, you can better understand why there&amp;rsquo;s latency in some scenarios, why the LLM came to certain solutions, and this will help you improve as your learn along the way.&lt;/p></description><content:encoded>
<![CDATA[<p>Inspired my by super boss <a href="https://twitter.com/rseroter">Richard Seroter</a> with his regular <a href="https://seroter.com/2023/09/28/daily-reading-list-september-28-2023-171/">daily reading list</a>, I decided to record and share my <em>tech watch</em>, every week (or so).
I always take notes of interesting articles I read for my own curiosity and to remember them when I need those references later on. But also to share them with <a href="https://lescastcodeurs.com/">Les Cast Codeurs podcast</a>!
So I hope it&rsquo;ll be interesting to my readers too!</p>
<ul>
<li>
<p><a href="https://www.honeycomb.io/llms-demand-observability-driven-development">LLMs Demand Observability-Driven Development<br />
</a>A great tribune from <a href="https://twitter.com/mipsytipsy">Charity Majors</a> on the importance of observability-driven development, in the wake of large language models. Developing LLM based solutions is typically not something you can do with a classical test-driven approach, as you only really get proper test data when you have it coming from production usage. Furthermore, LLMs are pretty much unpredictable and underterministic. But with observability in place, you can better understand why there&rsquo;s latency in some scenarios, why the LLM came to certain solutions, and this will help you improve as your learn along the way.</p>
</li>
<li>
<p><a href="https://blog.langchain.dev/building-chat-langchain-2/">How LangChain rebuilt their LLM documentation chatbot<br />
</a>For example: the choice of docs to parse: indexing source code didn&rsquo;t yield great results. Citing sources lets users dive deeper into the documentation and double check the LLM didn&rsquo;t hallucinate. Quality evaluation is important, to assess at each step of the process the impact of each change, each tweaks of your prompts, each change in the docs that are ingested. Also, how do you handle reindexing the documents, when there are changes in a document, when there are new pages to be indexed, or that disappear, to keep track of what has to be updated in the stored vector embeddings in vector store. A great trick about how to rephrase questions: sometimes you ask a question, ask a refinement, but you don&rsquo;t formulate a whole new question, so you can actually ask the LLM to reformulate a full question based on the conversation context, so as to find more meaningful similar text embeddings in the vector database.</p>
</li>
<li>
<p><a href="https://macoscontainers.org/">macOS containers<br />
</a>You can run all sorts of Linux flavors inside containers, on all platforms, and even Windows containers. But with the macOS containers projects, that you can install with Homebrew, you can also install and run macOS containers. It&rsquo;s still early days for the project, it seems, and there are limitated container support in macOS itself, but it sounds promising. Also, macOS containers only run on top of macOS  itself.</p>
</li>
<li>
<p><a href="https://adriano.fyi/posts/2023-09-24-choose-postgres-queue-technology/">Using PostgreSQL for queuing<br />
</a>With our without extensions, I see a lot of articles mentioning using PostgreSQL for everything! With the pgVector extension, for example, you can use Postgres as a vector database for storing parsed documents for your LLM use cases. In this article, the author suggests taking advantage of its pub/sub (with notify/listen) and row locking capabilities to implement queuing, and thus replacing other dedicated queuing components in your architecture.</p>
</li>
<li>
<p><a href="https://stable-diffusion-art.com/controlnet-sdxl/">Use ControlNet with StableDiffusion&rsquo;s SDXL<br />
</a>You&rsquo;ve probably all seen some cool images with some subliminal text appear, or with weird squares or spirals shapes, on social networks. This tutorial explains how you can guide StableDiffusion&rsquo;s SDXL model with ControlNet to shape particular picture generations, or to create pictures in the style of other pictures.</p>
</li>
<li>
<p><a href="https://huggingface.co/docs/transformers.js/index">Transformer.js<br />
</a>A JavaScript transformer implementation that allows to load HuggingFace models, and do predictions and other LLM tasks right in the browser.</p>
</li>
<li>
<p><a href="https://github.com/jbellis/jvector/">JVector<br />
</a>An open source Java project for fast vector search, used in Astra DB&rsquo;s vector search. This project was mentioned in TheNewStack&rsquo;s <a href="https://thenewstack.io/5-hard-problems-in-vector-search-and-how-cassandra-solves-them/">article</a> on how Astra DB solves 5 typical problems of vector search. So for those who want to embed an Java vector store in their LLM use cases, this might be an option to look into, besides Lucene, for example.</p>
</li>
<li>
<p><a href="https://www.marktechpost.com/2023/09/19/llms-knowledge-graphs/">Mixing LLMs and a Knowledge Graphs<br />
</a>Inherently, knowledge graphs have structured information and relationships, that LLM based projects can take advantage of. The article discusses different approaches and patterns to bind them together, to reduce hallucination, enhance transparency &amp; interpretability.</p>
</li>
<li>
<p><a href="https://andydote.co.uk/2023/09/19/tracing-is-better/">Tracing is better than logging!<br />
</a>It&rsquo;s often hard to figure out from logs what happened when a problem occurred. It&rsquo;s slightly better with structured logging to do some querying though. But with tracing, you can see correlations between traces, as they are nested, and see all the attributes you can attach to those spans, without mentioning the fact you can more easily understand where time is spent rather than just have a point in time like with a log statement.</p>
</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Discovering LangChain4J, the Generative AI orchestration library for Java developers</title><link>https://glaforge.dev/posts/2023/09/25/discovering-langchain4j/</link><pubDate>Mon, 25 Sep 2023 19:08:04 +0200</pubDate><guid>https://glaforge.dev/posts/2023/09/25/discovering-langchain4j/</guid><description>&lt;p>As I started my journey with Generative AI and Large Language Models, I&amp;rsquo;ve been overwhelmed with the omnipresence of Python.
Tons of resources are available with Python front and center. However, I&amp;rsquo;m a Java developer
(with a penchant for &lt;a href="https://groovy-lang.org/">Apache Groovy&lt;/a>, of course).
So what is there for me to create cool new Generative AI projects?&lt;/p>
&lt;p>When I built my first experiment with the
&lt;a href="https://cloud.google.com/vertex-ai/docs/generative-ai/start/quickstarts/api-quickstart">PaLM API&lt;/a>,
using the integration within the Google Cloud&amp;rsquo;s Vertex AI offering,
I called the available &lt;a href="https://cloud.google.com/vertex-ai/docs/reference/rest">REST API&lt;/a>,
from my &lt;a href="https://micronaut.io/">Micronaut&lt;/a> application.
I used Micronaut&amp;rsquo;s built-in mechanism to marshal / unmarshal the REST API constructs to proper classes.
Pretty straightfoward.&lt;/p></description><content:encoded>
<![CDATA[<p>As I started my journey with Generative AI and Large Language Models, I&rsquo;ve been overwhelmed with the omnipresence of Python.
Tons of resources are available with Python front and center. However, I&rsquo;m a Java developer
(with a penchant for <a href="https://groovy-lang.org/">Apache Groovy</a>, of course).
So what is there for me to create cool new Generative AI projects?</p>
<p>When I built my first experiment with the
<a href="https://cloud.google.com/vertex-ai/docs/generative-ai/start/quickstarts/api-quickstart">PaLM API</a>,
using the integration within the Google Cloud&rsquo;s Vertex AI offering,
I called the available <a href="https://cloud.google.com/vertex-ai/docs/reference/rest">REST API</a>,
from my <a href="https://micronaut.io/">Micronaut</a> application.
I used Micronaut&rsquo;s built-in mechanism to marshal / unmarshal the REST API constructs to proper classes.
Pretty straightfoward.</p>
<p>You can learn more about this first app in my previous articles on
<a href="https://glaforge.dev/posts/2023/06/08/creating-kids-stories-with-generative-ai/">generating kid stories</a>
and <a href="https://glaforge.dev/posts/2023/05/30/getting-started-with-the-palm-api-in-the-java-ecosystem/">how to get started with the PaLM API</a></p>
<p>But soon after, I discovered that the Vertex AI Java SDK, which covers all products and services of Vertex AI,
added support for the PaLM API thanks to a new
<a href="https://cloud.google.com/vertex-ai/docs/generative-ai/text/test-text-prompts#generative-ai-test-text-prompt-java">prediction service client</a> class.
I was happy and decided to try it! So here&rsquo;s how making a simple call to the LLM looks like from Groovy:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Grab</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;com.google.cloud:google-cloud-aiplatform:3.24.0&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">com.google.cloud.aiplatform.v1beta1.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">com.google.protobuf.Value</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">com.google.protobuf.util.JsonFormat</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>String instance <span style="color:#666">=</span>
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#39;&#39;&#39;{ &#34;prompt&#34;: &#34;Tell me more about Large Language Models&#34;}&#39;&#39;&#39;</span>
</span></span><span style="display:flex;"><span>String parameters <span style="color:#666">=</span> <span style="color:#4070a0">&#39;&#39;&#39;{
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">  &#34;temperature&#34;: 0.2,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">  &#34;maxOutputTokens&#34;: 256,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">  &#34;topP&#34;: 0.95,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">  &#34;topK&#34;: 40
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">}&#39;&#39;&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>String project <span style="color:#666">=</span> <span style="color:#4070a0">&#34;my-llm-java-demos&#34;</span>
</span></span><span style="display:flex;"><span>String location <span style="color:#666">=</span> <span style="color:#4070a0">&#34;us-central1&#34;</span>
</span></span><span style="display:flex;"><span>String publisher <span style="color:#666">=</span> <span style="color:#4070a0">&#34;google&#34;</span>
</span></span><span style="display:flex;"><span>String model <span style="color:#666">=</span> <span style="color:#4070a0">&#34;text-bison&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> predictionServiceSettings <span style="color:#666">=</span>
</span></span><span style="display:flex;"><span>    PredictionServiceSettings<span style="color:#666">.</span><span style="color:#4070a0">newBuilder</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">setEndpoint</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;${location}-aiplatform.googleapis.com:443&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">build</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> predictionServiceClient <span style="color:#666">=</span>
</span></span><span style="display:flex;"><span>    PredictionServiceClient<span style="color:#666">.</span><span style="color:#4070a0">create</span><span style="color:#666">(</span>predictionServiceSettings<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> endpointName <span style="color:#666">=</span>
</span></span><span style="display:flex;"><span>    EndpointName<span style="color:#666">.</span><span style="color:#4070a0">ofProjectLocationPublisherModelName</span><span style="color:#666">(</span>project<span style="color:#666">,</span> location<span style="color:#666">,</span> publisher<span style="color:#666">,</span> model<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> instanceValue <span style="color:#666">=</span> Value<span style="color:#666">.</span><span style="color:#4070a0">newBuilder</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>JsonFormat<span style="color:#666">.</span><span style="color:#4070a0">parser</span><span style="color:#666">().</span><span style="color:#4070a0">merge</span><span style="color:#666">(</span>instance<span style="color:#666">,</span> instanceValue<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> instances <span style="color:#666">=</span> <span style="color:#666">[</span>instanceValue<span style="color:#666">.</span><span style="color:#4070a0">build</span><span style="color:#666">()]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> parameterValueBuilder <span style="color:#666">=</span> Value<span style="color:#666">.</span><span style="color:#4070a0">newBuilder</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>JsonFormat<span style="color:#666">.</span><span style="color:#4070a0">parser</span><span style="color:#666">().</span><span style="color:#4070a0">merge</span><span style="color:#666">(</span>parameters<span style="color:#666">,</span> parameterValueBuilder<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> parameterValue <span style="color:#666">=</span> parameterValueBuilder<span style="color:#666">.</span><span style="color:#4070a0">build</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> resp <span style="color:#666">=</span> predictionServiceClient<span style="color:#666">.</span><span style="color:#4070a0">predict</span><span style="color:#666">(</span>endpointName<span style="color:#666">,</span> instances<span style="color:#666">,</span> parameterValue<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// resp[0].content
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>println resp<span style="color:#666">.</span><span style="color:#4070a0">predictionsList</span><span style="color:#666">.</span><span style="color:#4070a0">first</span><span style="color:#666">().</span><span style="color:#4070a0">structValue</span><span style="color:#666">.</span><span style="color:#4070a0">fieldsMap</span><span style="color:#666">[</span><span style="color:#4070a0">&#39;content&#39;</span><span style="color:#666">].</span><span style="color:#4070a0">stringValue</span> 
</span></span></code></pre></div><p>You create a PredictionServiceSettings, then an EndpointName, and a PredictionServiceClient to call its <code>predict()</code> method. Not overly complicated to set up.</p>
<p>However, there are really two things that I really dislike about this API:</p>
<ul>
<li>Why are we parsing some JSON strings and creating some Protobuf structures? This isn&rsquo;t very developer friendly to me.</li>
<li>And then, it also returns some generic Protobuf structure response, that I have to navigate through to find the relevant bits I&rsquo;m interested in, instead of letting me call something like <code>resp[0].content</code>.</li>
</ul>
<p>I&rsquo;d rather have a proper set of Java classes that represent my prompt, my LLM settings, the response, etc. I was a bit disappointed and preferred the approach I took with REST marshalling / unmarshalling in my Micronaut application &mdash; you can check the <a href="https://github.com/glaforge/bedtimestories">code on Github</a>.</p>
<p><figure>
  <a href="#img-3cc6d8fbcfa1ad8db6a5d47415311600">
    <img src="https://avatars.githubusercontent.com/u/132277850?v=4"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-3cc6d8fbcfa1ad8db6a5d47415311600">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://avatars.githubusercontent.com/u/132277850?v=4"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h2 id="here-comes-the-delight-with-langchain4j">Here comes the delight, with LangChain4J!</h2>
<p>If you&rsquo;re following the Generative AI field, you&rsquo;ll have come across the <a href="https://www.langchain.com/">LangChain</a> project. It&rsquo;s a Python (and Javascript) orchestrator framework to connect various building blocks: large language models, document loaders, text splitters, output parsers, vector stores to store text embeddings, tools, and prompts.</p>
<p>With just a few lines of code, you&rsquo;re able to create some great integrations to implement your Generative AI use cases, like for example following the <a href="https://www.langchain.com/use-case/retrieval">Retrieval Augmented Generation</a> pattern to create chat bots that talk with your documentation.</p>
<p>Remember that I&rsquo;m a Java developer? I played a bit with the Python version of LangChain (I didn&rsquo;t try the Javascript/Typescript variant) but I wasn&rsquo;t at ease with Python, and I didn&rsquo;t want to learn a whole new ecosystem to implement my Generative AI ideas.</p>
<p>Fortunately, that&rsquo;s when I discovered the open source <a href="https://github.com/langchain4j">LangChain4J</a> project! This is also an AI orchestrator framework, but for Java! It&rsquo;s very much inspired by the original LangChain project, but independent. So this is the perfect match for my programming language skills and Generative AI needs.</p>
<p>Now, let&rsquo;s compare our protobuf-<em>obstruse</em> example from earlier, with an equivalent one based on LangChain4J (this time I used the chat model instead of the text model):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Grab</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;dev.langchain4j:langchain4j-vertex-ai:0.22.0&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">dev.langchain4j.model.vertexai.*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>VertexAiChatModel vertexAiChatModel <span style="color:#666">=</span>
</span></span><span style="display:flex;"><span>    VertexAiChatModel<span style="color:#666">.</span><span style="color:#4070a0">builder</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">endpoint</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;us-central1-aiplatform.googleapis.com:443&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">project</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;my-llm-java-demos&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">location</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;us-central1&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">publisher</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;google&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">modelName</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;chat-bison@001&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">temperature</span><span style="color:#666">(</span><span style="color:#40a070">1.0</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">maxOutputTokens</span><span style="color:#666">(</span><span style="color:#40a070">256</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">topK</span><span style="color:#666">(</span><span style="color:#40a070">40</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">topP</span><span style="color:#666">(</span><span style="color:#40a070">0.95</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">maxRetries</span><span style="color:#666">(</span><span style="color:#40a070">3</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">build</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> response <span style="color:#666">=</span> vertexAiChatModel<span style="color:#666">.</span><span style="color:#4070a0">sendUserMessage</span><span style="color:#666">(</span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#34;What is the best Large Language Model?&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>println response<span style="color:#666">.</span><span style="color:#4070a0">text</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>It&rsquo;s very declarative and straightforward! I define my endpoint and my model settings with one builder. And then I just send messages to that chat model with just strings. And the response is also a simple string.</p>
<p>LangChain4J has won my heart!</p>
<h2 id="whats-next">What&rsquo;s next?</h2>
<p>I didn&rsquo;t stop there, I also built another Generative AI use case: I created a project that lets me ask questions about some documentation (in my case, I wanted to query the Apache Groovy documentation.) I&rsquo;ll tell you more about that project in a forthcoming article, as we dive deeper in LangChain4J, to cover text embeddings, vector stores, and more.</p>
<p>I&rsquo;ll be covering this topic on Generative AI with Java at
<a href="https://devoxx.be/talk/?id=4452">Devoxx Belgium</a> next week, and
<a href="https://devoxx.ma/talk/?id=4901">Devoxx Morocco</a> the following one.</p>
<p>But you can have a look already at some of the <a href="https://github.com/langchain4j/langchain4j-examples/tree/main/other-examples/src/main/java">more advanced examples</a>, to see how you can calculate vector embeddings locally with the all-MiniLM-L6-v2 embedding model, and store the vectors in a convenient in-memory vector store (<a href="http://all_minilm_l6_v2">link</a>), how to do text classification (<a href="https://github.com/langchain4j/langchain4j-examples/blob/main/other-examples/src/main/java/embedding/classification/EmbeddingModelTextClassifierExample.java">link</a>), how to talk chat with your documents with conversational retrieval chains (<a href="https://github.com/langchain4j/langchain4j-examples/blob/main/other-examples/src/main/java/ChatWithDocumentsExamples.java">link</a>).</p>
<p>LangChain4J is still young, but already pretty powerful, and offers integrations with VertexAI and OpenAI, with vector stores like <a href="https://www.trychroma.com/">ChromaDB</a>, <a href="https://www.pinecone.io/">Pinecone</a> or <a href="https://weaviate.io/">Weaviate</a> databases, and more.</p>
<p>Be sure to <a href="https://github.com/langchain4j">checkout LangChain4J</a> if you want to build your next Generative AI use case with Java!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Custom Environment Variables in Workflows</title><link>https://glaforge.dev/posts/2023/07/06/custom-environment-variables-in-workflows/</link><pubDate>Thu, 06 Jul 2023 14:44:24 +0200</pubDate><guid>https://glaforge.dev/posts/2023/07/06/custom-environment-variables-in-workflows/</guid><description>&lt;p>In addition to the built-in &lt;a href="https://cloud.google.com/workflows/docs/reference/environment-variables">environment variables available by default&lt;/a> in Google Cloud &lt;a href="https://cloud.google.com/workflows">Workflows&lt;/a> (like the project ID, the location, the workflow ID, etc.) it’s now possible to define your own &lt;a href="https://cloud.google.com/workflows/docs/use-environment-variables">custom environment variables&lt;/a>!&lt;/p>
&lt;p>Why is it useful and important? It’s particularly handy when you want to read information that is dependent on the deployment of your workflow, like, for example, information about the environment you’re running in. Is my workflow running in development, staging, or production environment? Then you can read your custom &lt;code>MY_ENVIRONMENT&lt;/code> variable, like you read the existing built-in environment variables. And you define such variables at deployment time.&lt;/p></description><content:encoded>
<![CDATA[<p>In addition to the built-in <a href="https://cloud.google.com/workflows/docs/reference/environment-variables">environment variables available by default</a> in Google Cloud <a href="https://cloud.google.com/workflows">Workflows</a> (like the project ID, the location, the workflow ID, etc.) it’s now possible to define your own <a href="https://cloud.google.com/workflows/docs/use-environment-variables">custom environment variables</a>!</p>
<p>Why is it useful and important? It’s particularly handy when you want to read information that is dependent on the deployment of your workflow, like, for example, information about the environment you’re running in. Is my workflow running in development, staging, or production environment? Then you can read your custom <code>MY_ENVIRONMENT</code> variable, like you read the existing built-in environment variables. And you define such variables at deployment time.</p>
<p>In our <a href="https://cloud.google.com/blog/topics/developers-practitioners/workflows-patterns-and-best-practices-part-3#:~:text=Plan%20for%20multi%2Denvironment%20orchestrations">best practices articles</a>, and in the more detailed article on <a href="https://cloud.google.com/blog/topics/developers-practitioners/multi-environment-service-orchestrations">multi-environment service orchestration</a>, my colleague <a href="https://atamel.dev/">Mete</a> and I had shared ways to implement such an approach. You have a workflow that orchestrates some API calls. But you want to have one single workflow definition that can run in different environments. There were a few approaches, each with pros and cons, to do that: by passing the API URLs as parameters of the workflow execution (but it doesn’t work for event-triggered workflows), by replacing some special text tokens before deployment, or even with some string replacement when deploying with Terraform.</p>
<p>But now, things are simpler! Let’s see how to <strong>define custom environment variables</strong> and how to <strong>access them</strong>.</p>
<h2 id="calling-an-api-endpoint-in-staging-or-in-prod">Calling an API endpoint in staging or in prod</h2>
<p>With custom environment variables, you can now deploy the exact same workflow definition, but with different variables, using the <code>sys.get_env()</code> built-in function:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>- call_a_service:
</span></span><span style="display:flex;"><span>    call: http.get
</span></span><span style="display:flex;"><span>    args:
</span></span><span style="display:flex;"><span>      url: <span style="color:#70a0d0">${</span><span style="color:#bb60d5">sys</span>.get_env(<span style="color:#4070a0">&#34;SERVICE_URL&#34;</span><span style="color:#70a0d0">}</span>
</span></span></code></pre></div><p>And on deployment, specify <code>SERVICE_URL</code> to point at the staging or the production URL of that service.</p>
<h2 id="setting-environment-variables">Setting environment variables</h2>
<p>Now that we’ve seen how to access an environment variable, let’s see how you can set it.</p>
<p>As explained in the documentation about <a href="https://cloud.google.com/workflows/docs/use-environment-variables">custom environment variables</a>, you can use different flags to define, update, delete those variables.</p>
<p>You can specify one or more variables with <code>--set-env-vars</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud workflows deploy WORKFLOW_NAME <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --set-env-vars <span style="color:#bb60d5">KEY1</span><span style="color:#666">=</span>VALUE1,KEY2<span style="color:#666">=</span>VALUE2
</span></span></code></pre></div><p>Use a file that contains all your custom environment variables (one <code>key:value</code> per line):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud workflows deploy WORKFLOW_NAME <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --env-vars-file FILE_PATH
</span></span></code></pre></div><p>You can also update vars with the <code>--update-env-vars</code> flag, remove some with <code>--remove-env-vars</code>, or delete them all with <code>--clear-env-vars</code>.</p>
<h2 id="bonus-tip-use-the-default-built-in-function-in-case-the-environment-variable-isnt-defined">Bonus tip: Use the <code>default()</code> built-in function in case the environment variable isn’t defined</h2>
<p>In case the environment variable wasn’t defined at deployment time, you can use the <code>default()</code> value method to set a default value, like in this example</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">logEnv</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>sys.log<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">text</span>:<span style="color:#bbb"> </span>${default(sys.get_env(&#34;ENVIRONMENT&#34;), &#34;PRODUCTION&#34;)}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Here, we’re logging the value of the environment, but if the <code>ENVIRONMENT</code> custom environment variable isn’t defined, by default, the value will be <code>PRODUCTION</code>.</p>
<h2 id="summary">Summary</h2>
<p>With custom environment variables, you can parameterize your workflows to tackle different use cases. One of the most frequently used ones is to use those environment variables to distinguish between different environments, like prod or staging. But you can also use them to configure different parameters of your workflow, like defining some configurable limits (number of retries), different endpoints or parameters for the services you call. You define your workflow once, and customize the deployment with different environment variables.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>From Bird to Elephant: Starting a New Journey on Mastodon</title><link>https://glaforge.dev/talks/2023/06/09/from-bird-to-elephant-starting-a-new-journey-on-mastodon/</link><pubDate>Fri, 09 Jun 2023 10:53:29 +0200</pubDate><guid>https://glaforge.dev/talks/2023/06/09/from-bird-to-elephant-starting-a-new-journey-on-mastodon/</guid><description>&lt;script async class="speakerdeck-embed" data-id="365d3694bd674bfa85d812d8c2fd32f9" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js">&lt;/script>
&lt;p>At Devoxx France and Devoxx Greece, I had the pleasure to talk about my new social media journey on &lt;a href="https://joinmastodon.org/">Mastodon&lt;/a>.
After a quick introduction about Mastodon and the &lt;a href="https://fediverse.party/">Fediverse&lt;/a>, I contrasted the key differences between Twitter and Mastodon.
Then I shared some advice on how to get started, how to chose an instance, or clients you can pick from.&lt;/p>
&lt;p>I moved on to important tips to get the best experience on the platform, and ensure to gather a great following:&lt;/p></description><content:encoded>
<![CDATA[<script async class="speakerdeck-embed" data-id="365d3694bd674bfa85d812d8c2fd32f9" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>At Devoxx France and Devoxx Greece, I had the pleasure to talk about my new social media journey on <a href="https://joinmastodon.org/">Mastodon</a>.
After a quick introduction about Mastodon and the <a href="https://fediverse.party/">Fediverse</a>, I contrasted the key differences between Twitter and Mastodon.
Then I shared some advice on how to get started, how to chose an instance, or clients you can pick from.</p>
<p>I moved on to important tips to get the best experience on the platform, and ensure to gather a great following:</p>
<ul>
<li>introduce yourself with a detailed bio and first #introduction post</li>
<li>create your profile before following others</li>
<li>configure your account so it can be discoveredn and recommended to others</li>
<li>contrary to Twitter, you can verify yourself, showing that you own the personal links you share</li>
</ul>
<p>Once your account is ready, if you are migrating from Twitter, you might want to find who among your friends have also migrated, so you can follow them in the fediverse.
I advise people not to delete their Twitter account, to avoid someone to pick up their old handle and to impersonate them.</p>
<p>Another aspect I like about the Mastodon platform is that it seems to care deeply about accessibility, and about people&rsquo;s possible troubles.
Putting <em>alt</em> tags for images is highly encouraged on Mastodon.
Or putting content warnings on text and images also helps prevent unwanted content to jump at your eyse unexpectedly.</p>
<p>In a second part of the presentation, I spoke about the various standards and APIs underlying the Fediverse and Mastodon:</p>
<ul>
<li><a href="https://www.w3.org/TR/activitypub/">ActivityPub</a> and <a href="https://www.w3.org/TR/activitystreams-core/">ActivityStream</a></li>
<li><a href="https://json-ld.org/">JSON-LD</a></li>
<li><a href="https://webfinger.net/">WebFinger</a></li>
<li><a href="http://microformats.org/">MicroFormats</a></li>
<li><a href="https://oauth.net/http-signatures/">HTTP Sigantures</a> and <a href="https://oauth.net/2/">OAuth 2</a>
I went through an exchange between a client and a server, mimicking the actual process when you send a message to another recipient.
This is basically how to implement a basic ActivityPub server, based on Eugen Rochko&rsquo;s great <a href="https://blog.joinmastodon.org/2018/06/how-to-implement-a-basic-activitypub-server/">post</a> on the topic.</li>
</ul>
<p>The third and final part of the presentation was a comcrete demo on how to implement your own bots on Mastodon.
I showed how to create a service (a <a href="https://micronaut.io/">Micronaut</a> application) that <a href="https://glaforge.dev/posts/2023/01/06/calculating-your-potential-reach-on-mastodon-with-google-cloud-workflows-orchestrating-the-mastodon-apis/">calculates the potential reach</a> of your posts.
You can play with the service <a href="https://stootistics.web.app/">online</a> and give it your account to see how popular your recent posts are.
And there&rsquo;s even an account (<a href="https://tooters.org/@getmyreach">@getmyreach@tooters.org</a>) you can ping on Mastodon to get back the most popular of your posts.
The code is available on <a href="https://github.com/glaforge/stootistics">Github</a> if you want to check it out.</p>
<p>You can check the recording of the talk in English from Devoxx Greece:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/DafHAmlzWUM?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>And in French at Devoxx France:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/_BaK9BNlUHg?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Creating kids stories with Generative AI</title><link>https://glaforge.dev/posts/2023/06/08/creating-kids-stories-with-generative-ai/</link><pubDate>Thu, 08 Jun 2023 12:12:42 +0200</pubDate><guid>https://glaforge.dev/posts/2023/06/08/creating-kids-stories-with-generative-ai/</guid><description>&lt;p>Last week, I wrote about how to &lt;a href="https://glaforge.dev/posts/2023/05/30/getting-started-with-the-palm-api-in-the-java-ecosystem/">get started with the PaLM API in the Java ecosystem&lt;/a>,
and particularly, how to overcome the lack of Java client libraries (at least for now) for the PaLM API, and how to properly authenticate.
However, what I didn&amp;rsquo;t explain was what I was building! Let&amp;rsquo;s fix that today, by telling you a story, a kid story!
Yes, I was using the trendy &lt;strong>Generative AI&lt;/strong> approach to generate bedtime stories for kids.&lt;/p></description><content:encoded>
<![CDATA[<p>Last week, I wrote about how to <a href="https://glaforge.dev/posts/2023/05/30/getting-started-with-the-palm-api-in-the-java-ecosystem/">get started with the PaLM API in the Java ecosystem</a>,
and particularly, how to overcome the lack of Java client libraries (at least for now) for the PaLM API, and how to properly authenticate.
However, what I didn&rsquo;t explain was what I was building! Let&rsquo;s fix that today, by telling you a story, a kid story!
Yes, I was using the trendy <strong>Generative AI</strong> approach to generate bedtime stories for kids.</p>
<p>Without further ado, let me introduce you to my little app: <a href="https://bed-time-stories.web.app/">bedtime stories</a>.</p>
<p><a href="https://bed-time-stories.web.app/"><figure>
  <a href="#img-991ea89b8355fc69be384f27c053673a">
    <img src="/img/bedtime/bedtime-stories-ui.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-991ea89b8355fc69be384f27c053673a">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bedtime/bedtime-stories-ui.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</a></p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Source Code</span>
      </div>
      <div class="admonition-content">
        <p>If you&rsquo;re interested in the source code, head over to the Github <a href="https://github.com/glaforge/bedtimestories">repository</a>:
it&rsquo;s implemented in <a href="https://groovy-lang.org/">Apache Groovy</a>, developed with the <a href="https://micronaut.io/">Micronaut</a> framework,
designed with the <a href="https://shoelace.style/">Shoelace</a> web components, and deployed on Google <a href="https://cloud.run/">Cloud Run</a>, the serverless container runtime.</p>
      </div>
    </div><h2 id="the-concept">The concept</h2>
<p>For a good story, we need 3 key ingredients:</p>
<ul>
<li>a <strong>character</strong> — the main protagonist of the story whose adventures are narrated, like a princess, an astronaut, a firefighter&hellip;</li>
<li>a <strong>setting</strong> — where (and potentially when) the action takes place, like a beautiful kingdom, a faraway planet, a mysterious haunted house&hellip;</li>
<li>a <strong>plot</strong> — a rough idea of what&rsquo;s going to happen in the story, like an evil darkness is menacing the kingdom, huge shooting stars are menacing the planet&hellip;</li>
</ul>
<p>In the UI, there are a few options to pick from, by default, but you can actually customise them at will, or better, come up with your own characters, settings, and plots.
You can play that game with your kids: <em>&ldquo;hey, who should be the hero of our story tonight?&rdquo;</em>. They may have an idea, or even a favorite character!</p>
<p>Then, just click the <code>Generate</code> button, and after 20s or so, you&rsquo;ll have a story ready!</p>
<h2 id="where-generative-ai-comes-in">Where Generative AI comes in</h2>
<p>Of course, the whole story is created thanks to Generative AI.
I used the <a href="https://cloud.google.com/vertex-ai/docs/generative-ai/learn/overview#palm-api">PaLM API</a> for that,
within my Google Cloud project, using the <a href="https://cloud.google.com/vertex-ai/">Vertex AI</a> suite of machine learning services.</p>
<p>For the characters, settings, and plots, I came up with a few ideas on my own.
But I felt like the choice was limited and would warrant some more creativity.
So I asked <a href="http://bard.google.com/">Bard</a> (powered by the PaLM API as well) to help me!
It&rsquo;s all about the <em>art of prompting</em>, of asking the right question to your favorite generative AI:</p>

    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Prompt</span>
      </div>
      <div class="admonition-content">
        <p>Here are a few characters of bedtime kid stories:</p>
<ul>
<li>a funny little princess with a strong character</li>
<li>a young astronaut exploring space</li>
<li>a fearless firefighter</li>
<li>a cute little cat with a long and silky fur</li>
<li>a gentle dragon with a colorful skin</li>
</ul>
<p>Suggest a list of other possible characters:</p>
      </div>
    </div><p>And lo and behold, I got a dozen suggestions that I then integrated in my character picker. Same thing for the settings and plots.
Interestingly, not only Bard (or PaLM) would give me suggestions, but it also gave me explanations on why those characters, settings and plots made sense.
So Generative AI is also here to help in the process of crafting your application, or your own prompts.</p>
<h2 id="the-story-generator-prompt">The story generator prompt</h2>
<p>The crux of this story is the main prompt that makes the requests to generate the actual story.
I wanted to have a familiar pattern or structure for my generated stories.
You&rsquo;ve probably heard about such common structures for narration, in 3 or 5 acts.
I came across this <a href="https://bubblecow.com/blog/importance-of-structure">website</a> about the 5-act approach,
with an exposition phase, the rising action, the climax, the falling action, and the final denouement.
So when crafting my prompt, first I started by telling PaLM who it was (a story teller), but then I also explained what a 5-act story act looks like,
and finally, I asked it to generate a story for my particular chosen trio of character, setting, and plot.
Here&rsquo;s my final prompt:</p>

    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Prompt</span>
      </div>
      <div class="admonition-content">
        <p>You are a creative and passionate story teller for kids.
Kids love hearing about the stories you invent.</p>
<p>Your stories are split into 5 acts:</p>
<ul>
<li>Act 1 : Sets up the story providing any contextual background the reader needs, but most importantly it contains the inciting moment. This incident sets the story in motion. An incident forces the protagonist to react. It requires resolution, producing narrative tension.</li>
<li>Act 2 : On a simplistic level this is the obstacles that are placed in the way of the protagonists as they attempt to resolve the inciting incident.</li>
<li>Act 3 : This is the turning point of the story. It is the point of the highest tension. In many modern narratives, this is the big battle or showdown.</li>
<li>Act 4 : The falling action is that part of the story in which the main part (the climax) has finished and you&rsquo;re heading to the conclusion. This is the calm after the tension of the climax.</li>
<li>Act 5 : This is the resolution of the story where conflicts are resolved and loose ends tied up. This is the moment of emotional release for the reader.</li>
</ul>
<p>Generate a kid story in 5 acts, where the protagonist is 
  <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi>c</mi><mi>h</mi><mi>a</mi><mi>r</mi><mi>a</mi><mi>c</mi><mi>t</mi><mi>e</mi><mi>r</mi></mrow><mo separator="true">,</mo><mi>w</mi><mi>h</mi><mi>e</mi><mi>r</mi><mi>e</mi><mi>t</mi><mi>h</mi><mi>e</mi><mi>a</mi><mi>c</mi><mi>t</mi><mi>i</mi><mi>o</mi><mi>n</mi><mi>t</mi><mi>a</mi><mi>k</mi><mi>e</mi><mi>s</mi><mi>p</mi><mi>l</mi><mi>a</mi><mi>c</mi><mi>e</mi></mrow><annotation encoding="application/x-tex">{character}, where the action takes place </annotation></semantics></math></span>

{setting} and ${plot}.</p>
      </div>
    </div><p>The fact of asking PaLM to structure the story that way also influences its textual output.
Not only did it create those 5 key parts, but it also added some bold act labels in its output, which I could filter to then split my string story into 5 smaller chunks.</p>
<h2 id="where-to-go-from-there">Where to go from there?</h2>
<p>I hope you enjoyed the journey so far, and that you got a chance to generate your own story and tell it to a happy kid!</p>
<p>However, for now at least, this is just a concept, so I&rsquo;m not sure whether I&rsquo;ll be developing it much further, but I&rsquo;d like to share possible ways to improve this application.</p>
<ul>
<li>As I explained, it&rsquo;s a story in 5 acts, so we could offer the story over 5 distinct pages that you would have to turn.
Vertex AI also features an image generation service (still in preview for now), so the <strong>stories could also be decorated with AI generated pictures</strong>!
(We can even ask PaLM to generate ideas of prompts for image generation.)</li>
<li>Currently, PaLM can generate up-to 1024 characters of output, but it has 8KB in input.
We can&rsquo;t generate a super long story, but since it&rsquo;s split in 5 acts, that can all fit in the input context window,
we could try to pass PaLM the whole generated story, and ask it 5 times to generate 1KB characters for each section, thus <strong>lengthening the whole story by a factor of 5</strong>.</li>
<li>To go even further, we could use the multilingual capabilities of large language models,
or at least just the Translate API, to offer <strong>translations of the app and the stories</strong> into different languages.</li>
<li>We could also imagine the app being able to narrate the story itself, by taking advantage of <strong>Text-to-Speech voice generation</strong>!
However you might miss on the great bonding opportunity with your kids when you tell them a story,
but on the other hand, kids could entertain themselves when you&rsquo;re busy by generating random stories on their own.</li>
<li>Maybe we could also <strong>save stories</strong> that pleased our kids (and reshare them with others),
as each generation, even with the same trio of character/setting/plot, can yield very diverse outcomes.</li>
</ul>
<p>So there are plenty options possible offered by Generative AI!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Just a handy command-line tool</title><link>https://glaforge.dev/posts/2023/06/07/just-a-handy-command-line-tool/</link><pubDate>Wed, 07 Jun 2023 15:48:02 +0200</pubDate><guid>https://glaforge.dev/posts/2023/06/07/just-a-handy-command-line-tool/</guid><description>&lt;p>When developing new projects on my laptop, I often run some commands over and over again.
Regardless of how far you&amp;rsquo;ve gone with your CI/CD pipelines, running commands locally without resorting to becoming a bash ninja can be pretty easy with&amp;hellip; &lt;code>just&lt;/code>!&lt;/p>
&lt;link rel="stylesheet" href="https://glaforge.dev/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&amp;#43;AD&amp;#43;9EeYXUTm&amp;#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
&lt;div class="admonition info">
&lt;div class="admonition-header">&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">&lt;path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/>&lt;/svg>
&lt;span>About Just&lt;/span>
&lt;/div>
&lt;div class="admonition-content">
&lt;p>&lt;a href="https://just.systems/">&lt;code>just&lt;/code>&lt;/a> is a handy way to save and run project-specific commands&lt;/p></description><content:encoded>
<![CDATA[<p>When developing new projects on my laptop, I often run some commands over and over again.
Regardless of how far you&rsquo;ve gone with your CI/CD pipelines, running commands locally without resorting to becoming a bash ninja can be pretty easy with&hellip; <code>just</code>!</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>About Just</span>
      </div>
      <div class="admonition-content">
        <p><a href="https://just.systems/"><code>just</code></a> is a handy way to save and run project-specific commands</p>
      </div>
    </div><p>It&rsquo;s a command-line tool that lets you define some commands to run (called recipes), in the form of a Makefile-inspired syntax.
It even allows you to define dependencies between the various tasks of your <code>justfile</code>.
It runs across all environments (Mac, Linux, Windows), and is quick to install.
It loads <code>.env</code> files in which you can define variables specific to your project (other developers can have the same <code>justfile</code> but have variables specific for their projects)</p>
<p>Without further ado, let&rsquo;s see it in action in my current project.</p>
<p>In my project, I have the following <code>justfile</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-make" data-lang="make"><span style="display:flex;"><span><span style="">set</span> <span style="">dotenv-load</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#06287e">alias r </span><span style="color:#666">:</span>= run
</span></span><span style="display:flex;"><span><span style="color:#06287e">alias b </span><span style="color:#666">:</span>= build
</span></span><span style="display:flex;"><span><span style="color:#06287e">alias d </span><span style="color:#666">:</span>= deploy
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#06287e">default</span><span style="color:#666">:</span> run
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#06287e">run</span><span style="color:#666">:</span>
</span></span><span style="display:flex;"><span>    ./gradlew -t run
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#06287e">build</span><span style="color:#666">:</span>
</span></span><span style="display:flex;"><span>    gcloud builds submit -t <span style="color:#bb60d5">$CLOUD_REGION</span>-docker.pkg.dev/<span style="color:#bb60d5">$PROJECT_ID</span>/containers/<span style="color:#bb60d5">$CONTAINER_NAME</span>:v1
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#06287e">deploy</span><span style="color:#666">:</span> build
</span></span><span style="display:flex;"><span>     gcloud run deploy bedtimestories --image<span style="color:#666">=</span><span style="color:#bb60d5">$CLOUD_REGION</span>-docker.pkg.dev/<span style="color:#bb60d5">$PROJECT_ID</span>/containers/<span style="color:#bb60d5">$CONTAINER_NAME</span>:v1
</span></span></code></pre></div><ul>
<li>The first instruction tells <code>just</code> to load an <code>.env</code> file.</li>
<li>The (optional) alias lines allow me to define shorcuts for commands that I run very often</li>
<li>There are three commands: <code>run</code>, <code>build</code>, and <code>deploy</code>:
<ul>
<li><code>run</code> will run my application locally with <code>gradle</code></li>
<li><code>build</code> will containerize my app with my <code>Dockerfile</code> on Google Cloud Build</li>
<li><code>deploy</code> depends on <code>build</code> and will deploy my container on Google Cloud Run</li>
</ul>
</li>
</ul>
<p>And now, I <em>just</em> run: <code>just run</code>, <code>just deploy</code>, or their shortcuts: <code>just r</code> or <code>just d</code>.</p>
<p>You also noticed the dollar variables which are interpolated from my <code>.env</code> file which contains the following variables:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#bb60d5">PROJECT_ID</span><span style="color:#666">=</span>some-project-id
</span></span><span style="display:flex;"><span><span style="color:#bb60d5">CLOUD_REGION</span><span style="color:#666">=</span>us-central1
</span></span><span style="display:flex;"><span><span style="color:#bb60d5">CONTAINER_NAME</span><span style="color:#666">=</span>some-container-name
</span></span></code></pre></div><p>It&rsquo;s <code>just</code> a new little handy tool in my toolbox!</p>
<p>Go check it out: <a href="https://just.systems/">just.systems</a>.
And have a look at the <a href="https://cheatography.com/linux-china/cheat-sheets/justfile/">cheat sheet</a> for more examples and syntax.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Getting started with the PaLM API in the Java ecosystem</title><link>https://glaforge.dev/posts/2023/05/30/getting-started-with-the-palm-api-in-the-java-ecosystem/</link><pubDate>Tue, 30 May 2023 15:42:32 +0200</pubDate><guid>https://glaforge.dev/posts/2023/05/30/getting-started-with-the-palm-api-in-the-java-ecosystem/</guid><description>&lt;p>Large Language Models (LLMs for short) are taking the world by storm,
and things like ChatGPT have become very popular and used by millions of users daily.
Google came up with its own chatbot called &lt;a href="https://bard.google.com/">Bard&lt;/a>,
which is powered by its ground-breaking &lt;a href="https://ai.google/discover/palm2/">PaLM 2&lt;/a> model and API.
You can also find and use the PaLM API from withing Google Cloud as well
(as part of &lt;a href="https://cloud.google.com/vertex-ai/docs/generative-ai/learn/overview">Vertex AI Generative AI&lt;/a> products)
and thus create your own applications based on that API.
However, if you look at the documentation, you&amp;rsquo;ll only find Python tutorials or notebooks,
or also explanations on how to make cURL calls to the API.
But since I&amp;rsquo;m a Java (and Groovy) developer at heart, I was interested in seeing how to do this from the Java world.&lt;/p></description><content:encoded>
<![CDATA[<p>Large Language Models (LLMs for short) are taking the world by storm,
and things like ChatGPT have become very popular and used by millions of users daily.
Google came up with its own chatbot called <a href="https://bard.google.com/">Bard</a>,
which is powered by its ground-breaking <a href="https://ai.google/discover/palm2/">PaLM 2</a> model and API.
You can also find and use the PaLM API from withing Google Cloud as well
(as part of <a href="https://cloud.google.com/vertex-ai/docs/generative-ai/learn/overview">Vertex AI Generative AI</a> products)
and thus create your own applications based on that API.
However, if you look at the documentation, you&rsquo;ll only find Python tutorials or notebooks,
or also explanations on how to make cURL calls to the API.
But since I&rsquo;m a Java (and Groovy) developer at heart, I was interested in seeing how to do this from the Java world.</p>
<h2 id="micronaut--groovy--cloud-run">Micronaut + Groovy + Cloud Run</h2>
<p>My use case was to create a simple application that generates bedtime kid stories, using the PaLM LLM.
I went ahead and decided to use <a href="https://micronaut.io">Micronaut</a> for my framework,
and <a href="https://groovy-lang.org">Apache Groovy</a> for my programming language.
I containerize and deploy my application on <a href="https://cloud.run">Cloud Run</a> on Google cloud.
And I use the <a href="https://cloud.google.com/run/docs/integrate/firebase-hosting">Cloud Run integration for Firebase</a>
to have a nice domain for my app, and to serve my static content from Firebase&rsquo;s CDN.
I won&rsquo;t cover these aspects too much in this article,
but I want to stress the important roadblock you might encounter: authentication.</p>
<h2 id="lets-get-started">Let&rsquo;s get started!</h2>
<p>First, you may not necessarily have access to the Generative AI services in Google Cloud.
For that, you&rsquo;ll need to <a href="https://cloud.google.com/ai/generative-ai">sign up</a> to join the Trusted Tester Program.
But once you have access, you&rsquo;ll be able to use the PaLM API programmatically for your own apps.</p>
<p>When experimenting with prompts to the LLM, you&rsquo;ll notice the handy sliding panel
that shows you how to interact with the API from code. But you only have the choice between Python and cURL.
That said, the cURL command helps you figure out how to call the API via REST:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    -X POST <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    -H <span style="color:#4070a0">&#34;Authorization: Bearer </span><span style="color:#007020;font-weight:bold">$(</span>gcloud auth print-access-token<span style="color:#007020;font-weight:bold">)</span><span style="color:#4070a0">&#34;</span> <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    -H <span style="color:#4070a0">&#34;Content-Type: application/json&#34;</span> <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    <span style="color:#4070a0">&#34;https://</span><span style="color:#70a0d0">${</span><span style="color:#bb60d5">API_ENDPOINT</span><span style="color:#70a0d0">}</span><span style="color:#4070a0">/v1/projects/</span><span style="color:#70a0d0">${</span><span style="color:#bb60d5">PROJECT_ID</span><span style="color:#70a0d0">}</span><span style="color:#4070a0">/locations/us-central1/publishers/google/models/</span><span style="color:#70a0d0">${</span><span style="color:#bb60d5">MODEL_ID</span><span style="color:#70a0d0">}</span><span style="color:#4070a0">:predict&#34;</span> -d <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    <span style="color:#4070a0">$&#39;{
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;instances&#34;: [
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        {
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;content&#34;: &#34;Write a kid story about an astronaut visiting another galaxy but facing problems with shooting stars&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        }
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    ],
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    &#34;parameters&#34;: {
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;temperature&#34;: 0.5,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;maxOutputTokens&#34;: 1000,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;topP&#34;: 0.8,
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#34;topK&#34;: 40
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    }
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    }&#39;</span>
</span></span></code></pre></div><p>We have the JSON structure in input, and if you call that command,
you&rsquo;ll get an output similar to the following one:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;predictions&#34;</span>: [
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;safetyAttributes&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;scores&#34;</span>: [
</span></span><span style="display:flex;"><span>          <span style="color:#40a070">0.10000000149011612</span>
</span></span><span style="display:flex;"><span>        ],
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;blocked&#34;</span>: <span style="color:#007020;font-weight:bold">false</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;categories&#34;</span>: [
</span></span><span style="display:flex;"><span>          <span style="color:#4070a0">&#34;Violent&#34;</span>
</span></span><span style="display:flex;"><span>        ]
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;content&#34;</span>: <span style="color:#4070a0">&#34;Once upon a time, there was a young astronaut called...&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Our Micronaut will have to marshall/unmarshall those input and output JSON documents.
But the tricky bit for me was authentication.
From the command-line, the embedded <code>gcloud</code> command makes use of an access token,
which grants you access to the PaLM API.
But from my Micronaut/Groovy code, I needed to find a way to authenticate as well.</p>
<h2 id="preparing-a-low-level-http-client-call">Preparing a low-level HTTP client call</h2>
<p>Let&rsquo;s craft the appropriate REST endpoint URI:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> uri <span style="color:#666">=</span> UriBuilder
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">of</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;/v1/projects/${projectId}/locations/us-central1/publishers/google/models/text-bison:predict&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">scheme</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;https&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">host</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;us-central1-aiplatform.googleapis.com&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">build</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>Currently, the API is only available in the <code>us-central1</code> region, so it&rsquo;s hard-coded.</p>
<p>Then we need to prepare the request:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> request <span style="color:#666">=</span> HttpRequest
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">POST</span><span style="color:#666">(</span>uri<span style="color:#666">,</span> <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>                <span style="color:#002070;font-weight:bold">instances:</span> <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>                  <span style="color:#666">[</span> <span style="color:#002070;font-weight:bold">content:</span> storyPrompt <span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>                <span style="color:#666">],</span>
</span></span><span style="display:flex;"><span>                <span style="color:#002070;font-weight:bold">parameters:</span> <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#002070;font-weight:bold">temperature:</span> <span style="color:#40a070">0.6</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#002070;font-weight:bold">maxOutputTokens:</span> <span style="color:#40a070">1000</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#002070;font-weight:bold">topP:</span> <span style="color:#40a070">0.8</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#002070;font-weight:bold">topK:</span> <span style="color:#40a070">40</span>
</span></span><span style="display:flex;"><span>                <span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">])</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">bearerAuth</span><span style="color:#666">(</span>token<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">accept</span><span style="color:#666">(</span>MediaType<span style="color:#666">.</span><span style="color:#4070a0">APPLICATION_JSON_TYPE</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">contentType</span><span style="color:#666">(</span>MediaType<span style="color:#666">.</span><span style="color:#4070a0">APPLICATION_JSON_TYPE</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>In a moment, we&rsquo;ll see how we can create the bearer <code>token</code> we use in the <code>bearerAuth()</code> call.
Here, we just send the prompt, with some parameters to say how creative we want the LLM answer to be.</p>
<p>Finally, we make the request:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> predictionResponse <span style="color:#666">=</span> client<span style="color:#666">.</span><span style="color:#4070a0">toBlocking</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">exchange</span><span style="color:#666">(</span>request<span style="color:#666">,</span> PredictionResponse<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">body</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">return</span> predictionResponse<span style="color:#666">.</span><span style="color:#4070a0">predictions</span><span style="color:#666">.</span><span style="color:#4070a0">first</span><span style="color:#666">().</span><span style="color:#4070a0">content</span>
</span></span></code></pre></div><p>I created some classes to unmarshall the resulting JSON:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">com.fasterxml.jackson.annotation.JsonProperty</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">io.micronaut.serde.annotation.Serdeable</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Serdeable</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">PredictionResponse</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@JsonProperty</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;predictions&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    List<span style="color:#666">&lt;</span>Prediction<span style="color:#666">&gt;</span> predictions
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Serdeable</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Prediction</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@JsonProperty</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;safetyAttributes&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    SafetyAttributes safetyAttributes
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@JsonProperty</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;content&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    String content
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Serdeable</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">SafetyAttributes</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><h2 id="authenticating">Authenticating</h2>
<p>When running my application locally, no problem, but once deployed, I needed to have a fresh bearer token.
I created a dedicated service account, with the minimum needed permissions:</p>
<ul>
<li><code>roles/aiplatform.user</code> to have the rights to call the PaLM API</li>
<li><code>roles/logging.logWriter</code> as your Cloud Run app needs to write some logs back to Cloud Logging</li>
</ul>
<p>This <a href="https://medium.com/google-cloud/generative-ai-palm-2-model-deployment-with-cloud-run-54e8a398b24b">article</a>
also nicely explains how to handle deployment to Cloud Run.</p>
<p>My Cloud Run service will be deployed with that service account.</p>
<p>Locally, on my laptop, I used the `GOOGLE_APPLICATION_CREDENTIALS&quot; approach,
by exporting a JSON key, and point at it via an environment variable:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#bb60d5">GOOGLE_APPLICATION_CREDENTIALS</span><span style="color:#666">=</span><span style="color:#4070a0">&#34;exported-key.json&#34;</span>
</span></span></code></pre></div><p>You can learn more about local development with
<a href="https://cloud.google.com/docs/authentication/provide-credentials-adc#local-dev">Application Default Credentials</a>.</p>
<p>So locally, we use that exported key, and locally we use a generated token from the restricted service account.
And to generate that token, I had to use the
<a href="https://github.com/googleapis/google-auth-library-java#google-auth-library-oauth2-http">google-auth-library-oauth2-http</a></p>
<p>Here&rsquo;s the missing snippet to do so:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> credentials <span style="color:#666">=</span> GoogleCredentials<span style="color:#666">.</span><span style="color:#4070a0">applicationDefault</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">createScoped</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;https://www.googleapis.com/auth/cloud-platform&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>credentials<span style="color:#666">.</span><span style="color:#4070a0">refreshIfExpired</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> token <span style="color:#666">=</span> credentials<span style="color:#666">.</span><span style="color:#4070a0">accessToken</span><span style="color:#666">.</span><span style="color:#4070a0">tokenValue</span>
</span></span></code></pre></div><p>To import that authentication library in my project, I defined its requirement in my <code>build.gradle</code> file:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>implementation<span style="color:#666">(</span><span style="color:#4070a0">&#39;com.google.auth:google-auth-library-credentials:1.17.0&#39;</span><span style="color:#666">)</span>
</span></span></code></pre></div><h2 id="voilà">Voilà!</h2>
<p>With the right authentication client library, I was able to create the beared token needed to authenticate
to the Vertex AI PaLM API, both locally on my laptop, and once deployed on Cloud Run as well.</p>
<p>Hopefully, when Google releases official Java client libraries,
it&rsquo;ll certainly be easier to interact with the PaLM API,
without having to create marshalling/unmarshalling code,
and will likely make it smoother to authenticate transparently.
So stay tuned!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Exploring Open Location Code</title><link>https://glaforge.dev/posts/2023/03/28/exploring-open-location-code/</link><pubDate>Tue, 28 Mar 2023 09:34:17 +0200</pubDate><guid>https://glaforge.dev/posts/2023/03/28/exploring-open-location-code/</guid><description>&lt;p>&lt;figure>
&lt;a href="#img-a3b04335fbe30698bff005c417f53821">
&lt;img src="https://glaforge.dev/img/misc/eiffel-tower-plus-code.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-a3b04335fbe30698bff005c417f53821">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/eiffel-tower-plus-code.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>When using Google Maps, you might have seen those strange little codes, as in the screenshot above.
This is a &lt;em>plus code&lt;/em>, or to use the more official name, an &lt;strong>Open Location Code&lt;/strong>.
It&amp;rsquo;s a way to encode a location in a short and (somewhat) memorable form.&lt;/p>
&lt;p>In countries like France, every house has an official address, so you can easily receive letters or get some parcel delivered. But there are countries where no such location system exists, so you have to resort to describing where you live (take this road, turn right after the red house, etc.)&lt;/p></description><content:encoded>
<![CDATA[<p><figure>
  <a href="#img-a3b04335fbe30698bff005c417f53821">
    <img src="/img/misc/eiffel-tower-plus-code.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-a3b04335fbe30698bff005c417f53821">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/eiffel-tower-plus-code.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>When using Google Maps, you might have seen those strange little codes, as in the screenshot above.
This is a <em>plus code</em>, or to use the more official name, an <strong>Open Location Code</strong>.
It&rsquo;s a way to encode a location in a short and (somewhat) memorable form.</p>
<p>In countries like France, every house has an official address, so you can easily receive letters or get some parcel delivered. But there are countries where no such location system exists, so you have to resort to describing where you live (take this road, turn right after the red house, etc.)</p>
<p>Of coursse, you could use GPS coordinates, but that&rsquo;s not very convenient to share, and nobody could remember a precise address. So there have been several attemps at creating systems that represent any location in the world,
like <a href="http://geohash.org/">GeoHash</a>, <a href="https://www.mapcode.com/">MapCode</a>, and other proprietary systems like 3-words.</p>
<p>Out of curiosity, I wanted to play with this geo-encoding approach, and decided to spend a few minutes playing with the available Java library (available on <a href="https://central.sonatype.com/artifact/com.google.openlocationcode/openlocationcode/1.0.4">Maven Central</a>), but using <a href="https://groovy-lang.org/">Apache Groovy</a>.</p>
<p>You&rsquo;ll find more links on the topic at the end of this article.</p>
<h2 id="playing-with-plus-codes-in-groovy">Playing with plus codes in Groovy</h2>
<p>Here&rsquo;s a little script that shows the library in action:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Grab</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;com.google.openlocationcode:openlocationcode:1.0.4&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">com.google.openlocationcode.OpenLocationCode</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Eiffel Tower
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> <span style="color:#666">(</span>lat<span style="color:#666">,</span> lon<span style="color:#666">)</span> <span style="color:#666">=</span> <span style="color:#666">[</span><span style="color:#40a070">48.8584</span><span style="color:#666">,</span> <span style="color:#40a070">2.29447</span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> eiffelTowerPlusCode <span style="color:#666">=</span> OpenLocationCode<span style="color:#666">.</span><span style="color:#4070a0">encode</span><span style="color:#666">(</span>lat<span style="color:#666">,</span> lon<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>println <span style="color:#4070a0">&#34;Eiffel Tower +code: ${eiffelTowerPlusCode}&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> decoded <span style="color:#666">=</span> OpenLocationCode<span style="color:#666">.</span><span style="color:#4070a0">decode</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;8FW4V75V+9Q&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>println <span style="color:#4070a0">&#34;Original coord: ${decoded.centerLatitude}, ${decoded.centerLongitude}&#34;</span>
</span></span></code></pre></div><p>(You can play and run the above script in the <a href="https://gwc-experiment.appspot.com/?g=groovy_4_0&amp;gist=4176e0ad13b396001c92ab5cd584b3d8">Groovy Web Console</a>)</p>
<h2 id="more-information">More information</h2>
<ul>
<li>The official <a href="https://maps.google.com/pluscodes/">Open Location Code website</a></li>
<li>The project <a href="https://github.com/google/open-location-code">open-location-code project on Github</a> that provides implementations of the algorithm in various programming languages</li>
<li>A <a href="https://github.com/google/open-location-code/wiki/Evaluation-of-Location-Encoding-Systems">comparison</a> of different geocoding / geohashing systems</li>
<li>The French wikipedia <a href="https://fr.wikipedia.org/wiki/Open_Location_Code">page on Open Location Code</a> shows visually how the world map is cut in smaller boxes, as you zoom in, and takes the example of the Eiffel Tower like in my script above</li>
<li>For reference, the English wikipedia <a href="https://en.wikipedia.org/wiki/Open_Location_Code">page</a>, but it&rsquo;s a little less detailed and visual</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>cURL's --json flag</title><link>https://glaforge.dev/posts/2023/03/22/curl-s-json-flag/</link><pubDate>Wed, 22 Mar 2023 07:14:06 +0100</pubDate><guid>https://glaforge.dev/posts/2023/03/22/curl-s-json-flag/</guid><description>&lt;p>As cURL was celebrating its &lt;a href="https://daniel.haxx.se/blog/2023/03/20/twenty-five-years-of-curl/">25th birthday&lt;/a>, I was reading Daniel Stenberg&amp;rsquo;s story behind the project, and discovered a neat little feature I hadn&amp;rsquo;t heard of before: the &lt;code>--json&lt;/code> flag! Daniel even &lt;a href="https://daniel.haxx.se/blog/2022/02/02/curl-dash-dash-json/">blogged&lt;/a> about it when it landed in cURL 7.82.0 last year.&lt;/p>
&lt;p>So what&amp;rsquo;s so cool about it? If you&amp;rsquo;re like me, you&amp;rsquo;re used to post some JSON data with the following verbose approach:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>curl --data &lt;span style="color:#4070a0">&amp;#39;{&amp;#34;msg&amp;#34;: &amp;#34;hello&amp;#34;}&amp;#39;&lt;/span> &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span> --header &lt;span style="color:#4070a0">&amp;#34;Content-Type: application/json&amp;#34;&lt;/span> &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span> --header &lt;span style="color:#4070a0">&amp;#34;Accept: application/json&amp;#34;&lt;/span> &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span> https://example.com
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You have to pass the data, and also pass headers to specify the content-type.
You can make it slightly shorter with the one-letter flags:&lt;/p></description><content:encoded>
<![CDATA[<p>As cURL was celebrating its <a href="https://daniel.haxx.se/blog/2023/03/20/twenty-five-years-of-curl/">25th birthday</a>, I was reading Daniel Stenberg&rsquo;s story behind the project, and discovered a neat little feature I hadn&rsquo;t heard of before: the <code>--json</code> flag! Daniel even <a href="https://daniel.haxx.se/blog/2022/02/02/curl-dash-dash-json/">blogged</a> about it when it landed in cURL 7.82.0 last year.</p>
<p>So what&rsquo;s so cool about it? If you&rsquo;re like me, you&rsquo;re used to post some JSON data with the following verbose approach:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl --data <span style="color:#4070a0">&#39;{&#34;msg&#34;: &#34;hello&#34;}&#39;</span> <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --header <span style="color:#4070a0">&#34;Content-Type: application/json&#34;</span> <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --header <span style="color:#4070a0">&#34;Accept: application/json&#34;</span> <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    https://example.com
</span></span></code></pre></div><p>You have to pass the data, and also pass headers to specify the content-type.
You can make it slightly shorter with the one-letter flags:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -d <span style="color:#4070a0">&#39;{&#34;msg&#34;: &#34;hello&#34;}&#39;</span> <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    -H <span style="color:#4070a0">&#34;Content-Type: application/json&#34;</span> <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    -H <span style="color:#4070a0">&#34;Accept: application/json&#34;</span> <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    https://example.com
</span></span></code></pre></div><p>But with the recent addition of this flag, it&rsquo;s much shorter, as you don&rsquo;t have to specify the mime types:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl --json <span style="color:#4070a0">&#39;{&#34;msg&#34;: &#34;hello&#34;}&#39;</span> https://example.com
</span></span></code></pre></div><p>It&rsquo;s available since version 7.82.0, and on my Mac laptop, I have version 7.86.0.</p>
<p>For reference, here&rsquo;s the excerpt of the manual that gives the details about the <code>--json</code> flag:</p>
<pre tabindex="0"><code>--json &lt;data&gt;
    (HTTP) Sends the specified JSON data in a POST request to the
    HTTP server. --json works as a shortcut for passing on these
    three options:

    --data [arg]
    --header &#34;Content-Type: application/json&#34;
    --header &#34;Accept: application/json&#34;

    There is no verification that the passed in data is actual
    JSON or that the syntax is correct.

    If you start the data with the letter @, the rest should be a
    file name to read the data from, or a single dash (-) if you
    want curl to read the data from stdin. Posting data from a file
    named &#39;foobar&#39; would thus be done with --json @foobar and to
    instead read the data from stdin, use --json @-.

    If this option is used more than once on the same command line,
    the additional data pieces will be concatenated to the previous
    before sending.

    The headers this option sets can be overridden with --header
    as usual.

    --json can be used several times in a command line

    Examples:

    curl --json &#39;{ &#34;drink&#34;: &#34;coffe&#34; }&#39; https://example.com
    curl --json &#39;{ &#34;drink&#34;:&#39; --json &#39; &#34;coffe&#34; }&#39; https://example.com
    curl --json @prepared https://example.com
    curl --json @- https://example.com &lt; json.txt
</code></pre><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Tip: Visualize output in the Groovy Console</title><link>https://glaforge.dev/posts/2023/03/21/tip-visualize-output-in-groovy-console/</link><pubDate>Tue, 21 Mar 2023 15:00:47 +0100</pubDate><guid>https://glaforge.dev/posts/2023/03/21/tip-visualize-output-in-groovy-console/</guid><description>&lt;p>For some scripting tasks, my favorite go-to tool is the &lt;strong>Groovy Console&lt;/strong>,
and writing code with &lt;a href="https://groovy-lang.org/">Apache Groovy&lt;/a>.
Usually, you just spill some &lt;code>println&lt;/code> calls all over the place to display some textual information.
But there&amp;rsquo;s a little known secret. Not really secret though,
as it&amp;rsquo;s properly &lt;a href="http://docs.groovy-lang.org/2.4.1/html/documentation/tools-groovyconsole.html#GroovyConsole-Visualizingscriptoutputresults">documented&lt;/a>.
It&amp;rsquo;s possible to display images (like &lt;code>BufferedImage&lt;/code> or its parent &lt;code>java.awt.Image&lt;/code>)
or all sorts of rich components (from the &lt;code>Swing&lt;/code> UI toolkit, like &lt;code>JPanel&lt;/code>, &lt;code>JLabel&lt;/code>, etc.)&lt;/p></description><content:encoded>
<![CDATA[<p>For some scripting tasks, my favorite go-to tool is the <strong>Groovy Console</strong>,
and writing code with <a href="https://groovy-lang.org/">Apache Groovy</a>.
Usually, you just spill some <code>println</code> calls all over the place to display some textual information.
But there&rsquo;s a little known secret. Not really secret though,
as it&rsquo;s properly <a href="http://docs.groovy-lang.org/2.4.1/html/documentation/tools-groovyconsole.html#GroovyConsole-Visualizingscriptoutputresults">documented</a>.
It&rsquo;s possible to display images (like <code>BufferedImage</code> or its parent <code>java.awt.Image</code>)
or all sorts of rich components (from the <code>Swing</code> UI toolkit, like <code>JPanel</code>, <code>JLabel</code>, etc.)</p>
<p>For example, to display an image in the output pane of my Groovy Console, I can load it up via an <code>ImageIcon</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">javax.swing.*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> url <span style="color:#666">=</span> <span style="color:#4070a0">&#34;https://pbs.twimg.com/profile_images/1590794600867893271/ttqX3njd_400x400.jpg&#34;</span><span style="color:#666">.</span><span style="color:#4070a0">toURL</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">ImageIcon</span><span style="color:#666">(</span>url<span style="color:#666">)</span>
</span></span></code></pre></div><p>For that purpose, you&rsquo;ll have to ensure that the <code>View &gt; Visualize Script Results</code> is enabled, as shown in the picture below:</p>
<p><figure>
  <a href="#img-99e005b6247319dd1a6ce48a06daa76b">
    <img src="/img/misc/groovy-console-visualize-output.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-99e005b6247319dd1a6ce48a06daa76b">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/groovy-console-visualize-output.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>As mentioned in the <a href="http://docs.groovy-lang.org/2.4.1/html/documentation/tools-groovyconsole.html#GroovyConsole-Visualizingscriptoutputresults">documentation</a>, you could for example display maps or lists as nice Swing <code>JTable</code>.
Or for some data visualisation, you could also used any Java libraries that output images or that can be embeded in Swing components,
like the venerable JFreeChart library (ie. here&rsquo;s a StackOverflow question that shows that JFreeChart charts can be embedded in Swing components like <code>JPanel</code>).</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Some custom VS Code settings</title><link>https://glaforge.dev/posts/2023/03/08/some-custom-vscode-settings/</link><pubDate>Wed, 08 Mar 2023 22:34:43 +0100</pubDate><guid>https://glaforge.dev/posts/2023/03/08/some-custom-vscode-settings/</guid><description>&lt;p>I regularly use both &lt;a href="https://www.jetbrains.com/idea/">IntelliJ IDEA&lt;/a> and &lt;a href="https://code.visualstudio.com/">Visual Studio Code&lt;/a> as my environments for developing.
But like all tools, we often need to personalise them to our liking, to feel at ease, or to be more productive.
As we read code more than we write, there are certain settings in your favorite editor to improve your reading experience. Today, I&amp;rsquo;ll share of the tweaks I&amp;rsquo;ve made to my VS Code settings.&lt;/p>
&lt;p>You can edit some of the settings by opening the UI of the settings dialog box, but you can also edit the &lt;code>JSON&lt;/code> file in which those settings are saved. On a Mac, for example, the &lt;code>settings.json&lt;/code> file is stored in &lt;code>~/Library/Application Support/Code/User/&lt;/code>.&lt;/p></description><content:encoded>
<![CDATA[<p>I regularly use both <a href="https://www.jetbrains.com/idea/">IntelliJ IDEA</a> and <a href="https://code.visualstudio.com/">Visual Studio Code</a> as my environments for developing.
But like all tools, we often need to personalise them to our liking, to feel at ease, or to be more productive.
As we read code more than we write, there are certain settings in your favorite editor to improve your reading experience. Today, I&rsquo;ll share of the tweaks I&rsquo;ve made to my VS Code settings.</p>
<p>You can edit some of the settings by opening the UI of the settings dialog box, but you can also edit the <code>JSON</code> file in which those settings are saved. On a Mac, for example, the <code>settings.json</code> file is stored in <code>~/Library/Application Support/Code/User/</code>.</p>
<p>Here are my current custom settings, compared to the default configuration:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;security.workspace.trust.untrustedFiles&#34;</span>: <span style="color:#4070a0">&#34;open&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;window.title&#34;</span>: <span style="color:#4070a0">&#34;${folderPath} ${separator} ${activeEditorShort}&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;breadcrumbs.enabled&#34;</span>: <span style="color:#007020;font-weight:bold">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;workbench.colorCustomizations&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;[Default Dark+]&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&#34;editor.lineHighlightBackground&#34;</span>: <span style="color:#4070a0">&#34;#404020FF&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&#34;editorLineNumber.activeForeground&#34;</span>: <span style="color:#4070a0">&#34;#ffff00&#34;</span>,
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;[Default Light+]&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&#34;editor.lineHighlightBackground&#34;</span>: <span style="color:#4070a0">&#34;#FFEEEE&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&#34;editorLineNumber.activeForeground&#34;</span>: <span style="color:#4070a0">&#34;#ff0000&#34;</span>,
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;workbench.tree.renderIndentGuides&#34;</span>: <span style="color:#4070a0">&#34;always&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;workbench.tree.indent&#34;</span>: <span style="color:#40a070">10</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;editor.fontSize&#34;</span>: <span style="color:#40a070">12</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;editor.fontFamily&#34;</span>: <span style="color:#4070a0">&#34;Fira Code&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;editor.fontLigatures&#34;</span>: <span style="color:#007020;font-weight:bold">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;editor.formatOnPaste&#34;</span>: <span style="color:#007020;font-weight:bold">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;editor.guides.bracketPairs&#34;</span>: <span style="color:#007020;font-weight:bold">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;files.trimTrailingWhitespace&#34;</span>: <span style="color:#007020;font-weight:bold">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;editor.mouseWheelZoom&#34;</span>: <span style="color:#007020;font-weight:bold">true</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Let&rsquo;s go through them:</p>
<ul>
<li><code>security.workspace.trust.untrustedFiles</code> — allows to open a file not part of the project without warning</li>
<li><code>window.title</code> — for a custom window title, with the root of the project, and the name of the current opened file</li>
<li><code>breadcrumbs.enabled</code> — to display a breadcrumbe to see where I&rsquo;m located in the file tree, and inside the file&rsquo;s structure itself</li>
<li><code>editor.lineHighlightBackground</code> and <code>editorLineNumber.activeForeground</code> — I&rsquo;ve customised the colors of the current line the cursor is on, as well as the line number in the gutter, to make it stand out more on my dark background</li>
<li><code>workbench.tree.renderIndentGuides</code> and <code>workbench.tree.indent</code> — adds the little vertical bars in the file explorer, to visually see the current level or depth in the directory structure, and specifies how far apart they should be</li>
<li><code>editor.fontSize</code> — customize the default font size (but it may already be 12 by default)</li>
<li><code>editor.fontFamily</code> and <code>editor.fontLigatures</code> — I&rsquo;m using Fire Code for my coding font, and I enable the ligatures to have nice looking operators</li>
<li><code>editor.formatOnPaste</code> — to automatically format the code that you pase, without having to manually reident it</li>
<li><code>editor.guides.bracketPairs</code> — for drawing a little vertical line that highlights the current scope of the block my cursor is in</li>
<li><code>files.trimTrailingWhitespace</code> — I like to trim the trailing whitespace at the end of a line automatically (to avoid some dummy commit because of a remaining space, for instance)</li>
<li><code>editor.mouseWheelZoom</code> — to allow mousewheel zoom in and out to increase / decrease font size</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>New blog location</title><link>https://glaforge.dev/posts/2023/03/06/new-blog-location/</link><pubDate>Mon, 06 Mar 2023 13:30:49 +0100</pubDate><guid>https://glaforge.dev/posts/2023/03/06/new-blog-location/</guid><description>&lt;p>I started blogging 20 years ago, in April 2003. My first blog engine was a PHP CMS, called &lt;a href="http://nucleuscms.org/">Nucleus&lt;/a>. I was hosting it on my ISP, at free.fr.&lt;/p>
&lt;p>Then in 2011, I wrote my own blog engine, called &lt;a href="https://github.com/glaforge/bloogaey">Bloogaey&lt;/a>, which was written in &lt;a href="https://groovy-lang.org/">Groovy&lt;/a>, using my little &lt;a href="http://gaelyk.appspot.com/">Gaelyk&lt;/a> web framework, and running on &lt;a href="https://cloud.google.com/appengine">App Engine&lt;/a>.&lt;/p>
&lt;p>As it became a bit painful to properly format my blog posts, and evolve my blog engine, I decided I should move to something that is more static, with a static site generator that eats Markdown files: I chose the &lt;a href="https://gohugo.io/">Hugo&lt;/a> static site generator that I used in some previous projects. And I&amp;rsquo;m now hosting the content of my blog in &lt;a href="https://github.com/glaforge/glaforge.github.io">Github Pages&lt;/a>, under the &lt;a href="https://glaforge.dev/">glaforge.dev&lt;/a> custom domain name.&lt;/p></description><content:encoded>
<![CDATA[<p>I started blogging 20 years ago, in April 2003. My first blog engine was a PHP CMS, called <a href="http://nucleuscms.org/">Nucleus</a>. I was hosting it on my ISP, at free.fr.</p>
<p>Then in 2011, I wrote my own blog engine, called <a href="https://github.com/glaforge/bloogaey">Bloogaey</a>, which was written in <a href="https://groovy-lang.org/">Groovy</a>, using my little <a href="http://gaelyk.appspot.com/">Gaelyk</a> web framework, and running on <a href="https://cloud.google.com/appengine">App Engine</a>.</p>
<p>As it became a bit painful to properly format my blog posts, and evolve my blog engine, I decided I should move to something that is more static, with a static site generator that eats Markdown files: I chose the <a href="https://gohugo.io/">Hugo</a> static site generator that I used in some previous projects. And I&rsquo;m now hosting the content of my blog in <a href="https://github.com/glaforge/glaforge.github.io">Github Pages</a>, under the <a href="https://glaforge.dev/">glaforge.dev</a> custom domain name.</p>
<h2 id="httpsglaforgedev"><a href="https://glaforge.dev/">https://glaforge.dev/</a></h2>
<p>I&rsquo;ll stop blogging on the old website, and will write my new articles on that new URL.</p>
<p>I&rsquo;ll try to redirect all the links back to this new location, and also update the RSS feed for those who are subscribed via Feedburner. But if you want to update your feed reader manually, here&rsquo;s the new URL for the feed: <a href="https://glaforge.dev/index.xml">https://glaforge.dev/index.xml</a></p>
<p><a href="https://glaforge.dev/"><figure>
  <a href="#img-6f45cb2e0ed5043791a5fdcebf468780">
    <img src="/img/misc/glaforge-dev-screenshot.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-6f45cb2e0ed5043791a5fdcebf468780">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/glaforge-dev-screenshot.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</a></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Google Cloud Workflows API automation, patterns, and best practices</title><link>https://glaforge.dev/talks/2023/02/01/google-cloud-workflows-api-automation-patterns-best-practices/</link><pubDate>Wed, 01 Feb 2023 23:31:37 +0100</pubDate><guid>https://glaforge.dev/talks/2023/02/01/google-cloud-workflows-api-automation-patterns-best-practices/</guid><description>&lt;ul>
&lt;li>Workflows at a glance, benefits, key features, use cases&lt;/li>
&lt;li>UI interface in Google Cloud console&lt;/li>
&lt;li>Deep dive into the Workflows syntax&lt;/li>
&lt;li>Workflows connectors&lt;/li>
&lt;li>Demos&lt;/li>
&lt;li>Patterns and best practices&lt;/li>
&lt;/ul>
&lt;script async class="speakerdeck-embed" data-id="9957977f37554ccdacf91f0859592bae" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js">&lt;/script></description><content:encoded>
<![CDATA[<ul>
<li>Workflows at a glance, benefits, key features, use cases</li>
<li>UI interface in Google Cloud console</li>
<li>Deep dive into the Workflows syntax</li>
<li>Workflows connectors</li>
<li>Demos</li>
<li>Patterns and best practices</li>
</ul>
<script async class="speakerdeck-embed" data-id="9957977f37554ccdacf91f0859592bae" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Calculating your potential reach on Mastodon with Google Cloud Workflows orchestrating the Mastodon APIs</title><link>https://glaforge.dev/posts/2023/01/06/calculating-your-potential-reach-on-mastodon-with-google-cloud-workflows-orchestrating-the-mastodon-apis/</link><pubDate>Fri, 06 Jan 2023 18:10:20 +0100</pubDate><guid>https://glaforge.dev/posts/2023/01/06/calculating-your-potential-reach-on-mastodon-with-google-cloud-workflows-orchestrating-the-mastodon-apis/</guid><description>&lt;p>With the turmoil around Twitter, like many, I’ve decided to look into &lt;a href="https://joinmastodon.org/">Mastodon&lt;/a>.
My friend &lt;a href="https://uwyn.net/@gbevin">Geert&lt;/a> is running his own Mastodon server,
and welcomed me on his instance at: &lt;a href="https://uwyn.net/@glaforge">uwyn.net/@glaforge&lt;/a>.&lt;/p>
&lt;p>With Twitter, you can access your &lt;a href="https://analytics.twitter.com/about">analytics&lt;/a> to know how your tweets are doing, how many views you’re getting.
Working in developer relations, it’s always interesting to get some insights into those numbers to figure out if what you’re sharing is interesting for your community.
But for various (actually good) reasons, Mastodon doesn’t offer such detailed analytics.
However, I wanted to see what the &lt;a href="https://docs.joinmastodon.org/api/">Mastodon APIs&lt;/a> offered.&lt;/p></description><content:encoded>
<![CDATA[<p>With the turmoil around Twitter, like many, I’ve decided to look into <a href="https://joinmastodon.org/">Mastodon</a>.
My friend <a href="https://uwyn.net/@gbevin">Geert</a> is running his own Mastodon server,
and welcomed me on his instance at: <a href="https://uwyn.net/@glaforge">uwyn.net/@glaforge</a>.</p>
<p>With Twitter, you can access your <a href="https://analytics.twitter.com/about">analytics</a> to know how your tweets are doing, how many views you’re getting.
Working in developer relations, it’s always interesting to get some insights into those numbers to figure out if what you’re sharing is interesting for your community.
But for various (actually good) reasons, Mastodon doesn’t offer such detailed analytics.
However, I wanted to see what the <a href="https://docs.joinmastodon.org/api/">Mastodon APIs</a> offered.</p>
<h2 id="how-to-calculate-your-potential-reach">How to calculate your potential reach</h2>
<p>Your <em>“toots”</em> (ie. your posts on Mastodon) can be “boosted” (equivalent of a retweet on Twitter).
Also, each actor on Mastodon has a certain <strong>number of followers</strong>.
So potentially, one of your toots can reach all your followers, as well as all the followers of the actors who reshare your toot.</p>
<p>So the maximum potential reach of one of your posts would correspond to the following equation:</p>
<pre tabindex="0"><code>potential_reach = 
    me.followers_count + 
    ∑ ( boosters[i].followers_count )
</code></pre><p>Let’s play with the Mastodon APIs to compute your reach</p>
<p>Fortunately, the Mastodon APIs allow you to get those numbers, albeit not with a single API call.
Let’s have a look at the interesting endpoints to get the potential reach of my most recent posts.</p>
<p>First of all, I’ll look up my account on the Mastodon instance that hosts me:</p>
<pre tabindex="0"><code>GET https://uwyn.net/api/v1/accounts/lookup?acct=glaforge
</code></pre><p>I pass my account name as a query parameter to the /accounts/lookup endpoint.</p>
<p>In return, I get a JSON document that contains various details about my account and me
(I’ll just show some of the interesting fields, not the whole payload):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="">id:</span> <span style="color:#062873;font-weight:bold">&#34;109314675907601286&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="">username:</span> <span style="color:#062873;font-weight:bold">&#34;glaforge&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="">acct:</span> <span style="color:#062873;font-weight:bold">&#34;glaforge&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="">display_name:</span> <span style="color:#062873;font-weight:bold">&#34;Guillaume Laforge&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="">...</span>
</span></span><span style="display:flex;"><span>    <span style="">note:</span> <span style="color:#062873;font-weight:bold">&#34;...&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="">url:</span> <span style="color:#062873;font-weight:bold">&#34;https://uwyn.net/@glaforge&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="">...</span>
</span></span><span style="display:flex;"><span>    <span style="">followers_count:</span> <span style="">878,</span>
</span></span><span style="display:flex;"><span>    <span style="">fields:</span> <span style="">[...]</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>I get two important pieces of information here: the followers_count gives me, you guessed it,
the number of followers my account has, thus the potential number of persons that can see my toots.
Also the id of my account, which I’ll need for some further API calls further down.</p>
<p>To get the most recent statuses I’ve posted, I’ll indeed need that account id for crafting the new URL I’ll call:</p>
<pre tabindex="0"><code>GET https://uwyn.net/api/v1/accounts/109314675907601286/statuses
</code></pre><p>This call will return a list of statuses (again, snipped less interesting part of the payload):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>[
</span></span><span style="display:flex;"><span>    <span style="">…</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="">id:</span> <span style="color:#062873;font-weight:bold">&#34;109620174916140649&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="">created_at:</span> <span style="color:#062873;font-weight:bold">&#34;2023-01-02T14:52:06.044Z&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="">…</span>
</span></span><span style="display:flex;"><span>        <span style="">replies_count:</span> <span style="">2,</span>
</span></span><span style="display:flex;"><span>        <span style="">reblogs_count:</span> <span style="">6,</span>
</span></span><span style="display:flex;"><span>        <span style="">favourites_count:</span> <span style="">6,</span>
</span></span><span style="display:flex;"><span>        <span style="">…</span>
</span></span><span style="display:flex;"><span>        <span style="">edited_at:</span> <span style="">null,</span>
</span></span><span style="display:flex;"><span>        <span style="">content:</span> <span style="color:#062873;font-weight:bold">&#34;...&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="">reblog:</span> <span style="">null,</span>
</span></span><span style="display:flex;"><span>        <span style="">…</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="">…</span>
</span></span><span style="display:flex;"><span>]
</span></span></code></pre></div><p>In each status object, you can see the number of replies, the number of times the post was reshared or favorited,
or whether it’s a reshared toot itself. So what’s interesting here is the reblogs_count number.</p>
<p>However, you don’t get more details about who reshared your toot.
So we’ll need some extra calls to figure that out!</p>
<p>So for each of your posts, you’ll have to call the following endpoint to know more about those “reblogs”:</p>
<pre tabindex="0"><code>GET https://uwyn.net/api/v1/statuses/109620174916140649/reblogged_by
</code></pre><p>This time, you’ll get a list of all the persons who reshared your post:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>[
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="">id:</span> <span style="color:#062873;font-weight:bold">&#34;123456789&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="">username:</span> <span style="color:#062873;font-weight:bold">&#34;...&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="">acct:</span> <span style="color:#062873;font-weight:bold">&#34;...&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="">display_name:</span> <span style="color:#062873;font-weight:bold">&#34;...&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="">...</span>
</span></span><span style="display:flex;"><span>        <span style="">followers_count:</span> <span style="">7,</span>
</span></span><span style="display:flex;"><span>        <span style="">...</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="">...</span>
</span></span><span style="display:flex;"><span>]
</span></span></code></pre></div><p>And as you can see the details of those persons also have the followers_count field,
that tells the number of people that follow them.</p>
<p>So now, we have all the numbers we need to calculate the potential reach of our toots:
your own number of followers, and the number of followers of all those who reshared!
It doesn’t mean that your toots will actually be viewed that many times,
as one doesn’t necessarily read each and every toots on their timelines,
but at least, that’s an approximation of the maximum reach you can get.</p>
<h2 id="automating-the-potential-reach-calculation-with-web-api-orchestration">Automating the potential reach calculation with Web API orchestration</h2>
<p>Initially I played with both cURL and a little Apache Groovy <a href="https://gwc-experiment.appspot.com/?g=groovy_4_0&amp;codez=eJx1kl9PwjAUxd_3KRqeNjUtoAlsCTHGhAfjg0F8MoaUcQczY3dp78DF-N0tLRv_xp66c-85t_216bpARWypEDcV_9aY8xvPW0DCdms2Yjls2YtZvmelKkD5gcfMZzsIkT5UZpo6K6JCR0KU2yrnOZCQRSo2PaFJUqlBi143vB_2hmE4fOiH_cEg7HZsRiGVhsXUJJkYO94qU_ghf5_PCT8mr37AyYiB14x-Q02gxphluAWljf8QxmUcY5kTT-ryzP6zx4h1m4Sx3KBKCc68iZFLq7eZJjDPcHlmUU487rcGp08bUDWyW9YRrraExWxeHcNwA56UktUlk5PANjJNagPm2e5pdJnOY9MCMbFflraTYn9cl2s_2J9IAZUqZ5_2CSQ1vOiU5Z2r1mlR2125nj206BirqxCSzJwyPgRdOZqzFEiQU7qzyXjVOtVQvxLhfXneP1NZD_M">script</a>
to better understand the Mastodon APIs to figure out how to chain them to get to the expected result.
Then I decided to automate that series of Web API calls using an API orchestrator: <a href="https://cloud.google.com/workflows">Google Cloud Workflows</a>.</p>
<p>To recap, we need to:</p>
<ul>
<li>Get the details of your account</li>
<li>Get the recent posts for that account</li>
<li>Get all the followers count for each person who reshared each post</li>
</ul>
<p>Let’s have a look at this piece by piece:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">main</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">params</span>:<span style="color:#bbb"> </span>[input]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">account_server_vars</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">account</span>:<span style="color:#bbb"> </span>${input.account}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">server</span>:<span style="color:#bbb"> </span>${input.server}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">prefix</span>:<span style="color:#bbb"> </span>${&#34;https://&#34; + server + &#34;/api/v1&#34;}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">impact_map</span>:<span style="color:#bbb"> </span>{}<span style="color:#bbb">
</span></span></span></code></pre></div><p>First, the workflow takes an account and server arguments, in my case that is glaforge and uwyn.net.
And I’m defining a variable with the base path of the Mastodon API, and a dictionary to hold the data for each toot.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">find_account_id</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>http.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb"> </span>${prefix + &#34;/accounts/lookup&#34;}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">query</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">acct</span>:<span style="color:#bbb"> </span>${account}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>account_id_lookup<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">account_id_var</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">account_id</span>:<span style="color:#bbb"> </span>${account_id_lookup.body.id}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">followers_count</span>:<span style="color:#bbb"> </span>${account_id_lookup.body.followers_count}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Above, I’m doing an account lookup, to get the id of the account, but also the followers count.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">get_statuses</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>http.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb"> </span>${prefix + &#34;/accounts/&#34; + account_id + &#34;/statuses&#34;}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">query</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">limit</span>:<span style="color:#bbb"> </span><span style="color:#40a070">100</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">exclude_reblogs</span>:<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">true</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>statuses<span style="color:#bbb">
</span></span></span></code></pre></div><p>We get the list of most recent toots.</p>
<p>Now things get more interesting, as we need to iterate over all the statuses. We’ll do so in parallel, to save some time:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">iterate_statuses</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">parallel</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">shared</span>:<span style="color:#bbb"> </span>[impact_map]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">for</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">value</span>:<span style="color:#bbb"> </span>status<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">in</span>:<span style="color:#bbb"> </span>${statuses.body}<span style="color:#bbb">
</span></span></span></code></pre></div><p>To parallelize the per-status calls, we just need to state it’s parallel,
and that the variable we’ll keep our data in is a shared variable that needs to be accessed in parallel.
Next, we define the steps for each parallel iteration:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">counter_var</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">impact</span>:<span style="color:#bbb"> </span>${followers_count}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">fetch_reblogs</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>http.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb"> </span>${prefix + &#34;/statuses/&#34; + status.id + &#34;/reblogged_by&#34;}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>reblogs<span style="color:#bbb">
</span></span></span></code></pre></div><p>Above, we get the list of people who reshared our post. And for each of these accounts,
we’re incrementing our impact counter with the number of their followers.
It’s another loop, but that doesn’t need to be done in parallel, as we’re not calling any API:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">iterate_reblogs</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">for</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">value</span>:<span style="color:#bbb"> </span>reblog<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">in</span>:<span style="color:#bbb"> </span>${reblogs.body}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">increment_reblog</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>- <span style="color:#062873;font-weight:bold">impact</span>:<span style="color:#bbb"> </span>${impact + reblog.followers_count}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">update_impact_map</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">impact_map[status.url]</span>:<span style="color:#bbb"> </span>${impact}<span style="color:#bbb">
</span></span></span></code></pre></div><p>And we finish the workflow by returning the data:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">returnOutput</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">id</span>:<span style="color:#bbb"> </span>${account_id}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">account</span>:<span style="color:#bbb"> </span>${account}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">server</span>:<span style="color:#bbb"> </span>${server}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">followers</span>:<span style="color:#bbb"> </span>${followers_count}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">impact</span>:<span style="color:#bbb"> </span>${impact_map}<span style="color:#bbb">
</span></span></span></code></pre></div><p>This will return an output similar to this:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;account&#34;</span>: <span style="color:#4070a0">&#34;glaforge&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;followers&#34;</span>: <span style="color:#40a070">878</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;id&#34;</span>: <span style="color:#4070a0">&#34;109314675907601286&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;impact&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;https://uwyn.net/@glaforge/109422399389341013&#34;</span>: <span style="color:#40a070">945</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;https://uwyn.net/@glaforge/109462120695384207&#34;</span>: <span style="color:#40a070">1523</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;https://uwyn.net/@glaforge/109494881278500194&#34;</span>: <span style="color:#40a070">121385</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;https://uwyn.net/@glaforge/109495686235916646&#34;</span>: <span style="color:#40a070">878</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;https://uwyn.net/@glaforge/109516968335141401&#34;</span>: <span style="color:#40a070">1002</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;https://uwyn.net/@glaforge/109523829424569844&#34;</span>: <span style="color:#40a070">878</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;https://uwyn.net/@glaforge/109528949144442301&#34;</span>: <span style="color:#40a070">896</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;https://uwyn.net/@glaforge/109620174916140649&#34;</span>: <span style="color:#40a070">1662</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;https://uwyn.net/@glaforge/109621803885287542&#34;</span>: <span style="color:#40a070">1523</span>,
</span></span><span style="display:flex;"><span>    <span style="">...</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;server&#34;</span>: <span style="color:#4070a0">&#34;uwyn.net&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>With this little workflow, I can check how my toots are doing on this new social media! As next steps,
you might want to check out how to get started with API orchestration with Google Cloud Workflows,
<a href="https://cloud.google.com/workflows/docs/create-workflow-console">in the cloud console</a>,
or <a href="https://cloud.google.com/workflows/docs/create-workflow-gcloud">from the command-line</a>.
And to go further, potentially, it might be interesting to
<a href="https://glaforge.dev/posts/2022/02/09/schedule-a-workflow-execution/">schedule a workflow execution</a>
with <a href="https://cloud.google.com/scheduler">Cloud Scheduler</a>.
We could also imagine storing those stats in a database (perhaps <a href="https://cloud.google.com/bigquery">BigQuery</a> for some analytics,
or simply <a href="https://cloud.google.com/firestore">Firestore</a>
or <a href="https://cloud.google.com/sql">CloudSQL</a>), to see how your impact evolves over time.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Workflows patterns and best practices — Part 3</title><link>https://glaforge.dev/posts/2022/12/06/workflows-patterns-and-best-practices-part-3/</link><pubDate>Tue, 06 Dec 2022 14:12:25 +0100</pubDate><guid>https://glaforge.dev/posts/2022/12/06/workflows-patterns-and-best-practices-part-3/</guid><description>&lt;p>This is a three-part series of posts, in which we summarize Workflows and service orchestration patterns. In this third and final post, we talk about managing workflow life cycles and the benefits of using Firestore with Workflows. &lt;/p>
&lt;h3 id="use-subworkflows-and-terraform-to-manage-workflow-definitions">Use subworkflows and Terraform to manage workflow definitions&lt;/h3>
&lt;p>If you&amp;rsquo;re not careful, the workflow definitions you create with YAML or JSON can get out of hand pretty quickly. While it is possible to use subworkflows to define snippets of a workflow that can be reused from multiple workflows, Workflows does not support importing these subworkflows. Thankfully, there are other tools, such as Terraform, that can help.&lt;/p></description><content:encoded>
<![CDATA[<p>This is a three-part series of posts, in which we summarize Workflows and service orchestration patterns. In this third and final post, we talk about managing workflow life cycles and the benefits of using Firestore with Workflows. </p>
<h3 id="use-subworkflows-and-terraform-to-manage-workflow-definitions">Use subworkflows and Terraform to manage workflow definitions</h3>
<p>If you&rsquo;re not careful, the workflow definitions you create with YAML or JSON can get out of hand pretty quickly. While it is possible to use subworkflows to define snippets of a workflow that can be reused from multiple workflows, Workflows does not support importing these subworkflows. Thankfully, there are other tools, such as Terraform, that can help.</p>
<p>In our <a href="https://cloud.google.com/blog/topics/developers-practitioners/deploying-multi-yaml-workflows-definitions-terraform">Deploying multi-YAML Workflows definitions with Terraform</a> post, we showed how to use Terraform to define a workflow and import it into a Terraform configuration file. We further showed how to also import a subworkflow in the same workflow definition. This makes it easier to manage workflows and subworkflow definitions.</p>
<p>When you&rsquo;re defining a workflow, make sure you have a strategy to define and reuse subworkflows with Terraform or some other tool.</p>
<h3 id="gitops-your-service-orchestrations">GitOps your service orchestrations</h3>
<p><a href="https://opengitops.dev/">GitOps</a> takes DevOps best practices used for application development (such as version control and CI/CD) and applies them to infrastructure automation. Service orchestrations, which have their own definition files and deployment cycles, can benefit from a GitOps approach as well.</p>
<p>In our <a href="https://cloud.google.com/blog/topics/developers-practitioners/gitsops-service-orchestration">GitOps your service orchestrations</a> post, we showed how to use Cloud Build to manage an automated and staged rollout of workflow changes with tests along the way to minimize risk.</p>
<p><figure>
  <a href="#img-602a2146370e39cb953514e05f02c59f">
    <img src="/img/workflows-patterns/GitOps_Blog_1.max-800x800.max-800x800.png"
      alt="/img/workflows-patterns/GitOps_Blog_1.max-800x800.max-800x800.png"
       />
  </a>
  <figcaption>/img/workflows-patterns/GitOps_Blog_1.max-800x800.max-800x800.png</figcaption>
</figure>
<div class="lightbox" id="img-602a2146370e39cb953514e05f02c59f">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-patterns/GitOps_Blog_1.max-800x800.max-800x800.png"
    alt="/img/workflows-patterns/GitOps_Blog_1.max-800x800.max-800x800.png"
     />
  <div class="lightbox-caption">/img/workflows-patterns/GitOps_Blog_1.max-800x800.max-800x800.png</div>
</div>
</p>
<h3 id="plan-for-multi-environment-orchestrations">Plan for multi-environment orchestrations</h3>
<p>While GitOps helps to manage the deployment lifecycle of a workflow, sometimes you need to make changes to the workflow before deploying to different environments. That means you need to design workflows with multiple environments in mind. For example, instead of hardcoding the URLs called from the workflow, replace the URLs with staging and production URLs depending on where the workflow is being deployed.</p>
<p>In our <a href="https://cloud.google.com/blog/topics/developers-practitioners/multi-environment-service-orchestrations">Multi-environment service orchestrations</a> post, we showed three different ways of replacing URLs in a workflow: passing URLs as runtime arguments, using Cloud Build to deploy multiple versions, and using Terraform to deploy multiple versions.</p>
<p><figure>
  <a href="#img-3376d963589d1cc1540a9cd57d698a31">
    <img src="/img/workflows-patterns/GOB2_-_2.max-700x700.max-700x700.png"
      alt="/img/workflows-patterns/GOB2_-_2.max-700x700.max-700x700.png"
       />
  </a>
  <figcaption>/img/workflows-patterns/GOB2_-_2.max-700x700.max-700x700.png</figcaption>
</figure>
<div class="lightbox" id="img-3376d963589d1cc1540a9cd57d698a31">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-patterns/GOB2_-_2.max-700x700.max-700x700.png"
    alt="/img/workflows-patterns/GOB2_-_2.max-700x700.max-700x700.png"
     />
  <div class="lightbox-caption">/img/workflows-patterns/GOB2_-_2.max-700x700.max-700x700.png</div>
</div>
</p>
<h3 id="manage-external-state-with-firestore">Manage external state with Firestore</h3>
<p>You define workflows YAML/JSON as the recipe and then execute it with optional runtime arguments as an individual isolated instance of that recipe. Sometimes, you need to store some state (typically a key/value pair) in a step from one workflow execution and later read that state in another step from another workflow execution. There&rsquo;s no intrinsic key/value store in Workflows. However, you can use Firestore to store and read key/value pairs from Workflows.</p>
<p>In our <a href="https://medium.com/google-cloud/worklows-state-management-with-firestore-99237f08c5c5">Workflows state management with Firestore</a> post and its associated <a href="https://github.com/GoogleCloudPlatform/workflows-demos/tree/master/state-management-firestore">sample</a>, we showed a couple of subworkflows to put and get key/value pairs from Workflows with Firestore. This pattern is very useful when you need to manage some state in your workflow. </p>
<h3 id="workflows-for-reliable-work-and-firestore-for-reactive-ui">Workflows for reliable work and Firestore for reactive UI</h3>
<p>You can count on Workflows to perform some long-running work reliably and as an admin, check the status of the workflow and the current running step using the Google Cloud console or the Workflows API. However, how do you keep end users up to date about the status of the workflow? To have this kind of reactive UI, you can have your workflow write its status to a Firestore document and have Firestore notify connected end users in real time.</p>
<p>At Google I/O, we demonstrated this pattern with two examples. In the <a href="https://github.com/GoogleCloudPlatform/smart-expenses">expense report application</a>, the UI updates the status of the approval process both for the employee and the manager. In the <a href="https://github.com/GoogleCloudPlatform/workflows-demos/tree/master/callback-translation">translation application using callbacks</a>, Firestore is used to show the status of an ongoing translation.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/l3aMs00ziYA?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<hr />
<p>This wraps up our three-part series. For questions or feedback, or if you want to share your own best practices and patterns, feel free to reach out to us on Twitter @<a href="https://twitter.com/meteatamel">meteatamel</a> and @<a href="https://twitter.com/glaforge">glaforge</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Turning a Website Into a Desktop Application</title><link>https://glaforge.dev/posts/2022/11/29/turning-a-website-into-a-desktop-application/</link><pubDate>Tue, 29 Nov 2022 12:47:19 +0100</pubDate><guid>https://glaforge.dev/posts/2022/11/29/turning-a-website-into-a-desktop-application/</guid><description>&lt;p>Probably like most of you, my dear readers, I have too many browser windows open,
with tons of tabs for each window. But there are always apps I come back to very often,
like my email (professional &amp;amp; personal), my calendar, my chat app, or even social media
sites like Mastodon or Twitter. You can switch from window to window with &lt;code>CTRL/CMD-Tab&lt;/code>,
but you also have to move between tabs potentially. But for the most common webapps or
websites I’m using, I wanted to have a dedicated desktop application.&lt;/p></description><content:encoded>
<![CDATA[<p>Probably like most of you, my dear readers, I have too many browser windows open,
with tons of tabs for each window. But there are always apps I come back to very often,
like my email (professional &amp; personal), my calendar, my chat app, or even social media
sites like Mastodon or Twitter. You can switch from window to window with <code>CTRL/CMD-Tab</code>,
but you also have to move between tabs potentially. But for the most common webapps or
websites I’m using, I wanted to have a dedicated desktop application.</p>
<p>Initially, I was on the lookout for a Mac specific approach, as I’ve been a macOS users
for many years. So I found some Mac-specific apps that can handle that. This website mentions
<a href="https://www.makeuseof.com/tag/website-desktop-mac-app/">5 approaches for macOS</a>, including
free, freemium, non-free apps, like Fluid, Applicationize (creating a Chrome extension),
Web2Desk, or Unite. However, some of them create big hundred-mega wrappers. Another approach
on Macs was using Automator, to create a pop-up window, but that’s just a pop-up, not a real app.
There are also some promising open source projects like <a href="https://tauri.app/">Tauri</a>
and <a href="https://github.com/nativefier/nativefier">Nativefier</a> which seem promising.</p>
<p>Fortunately, there’s a cool feature from Chrome, that should work across all OSses,
and not just macOS. So if you’re on Linux or Windows, please read on.
The websites you’ll turn into applications don’t even need to be PWAs (Progressive Web Apps).</p>
<p>Here’s how to proceed:</p>
<p>First, navigate to your website you want to transform into an application with your Chrome browser.</p>
<p>Click on the triple dots in the top right corner, then <code>More Tools</code>, and finally <code>Create Shorctut</code>:</p>
<p><figure>
  <a href="#img-aaaf5799ac8b56adca92703c22ea6ec3">
    <img src="/img/chrome-to-app/web2app-01-create-shortcut.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-aaaf5799ac8b56adca92703c22ea6ec3">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/chrome-to-app/web2app-01-create-shortcut.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>It will then let you customise the name of the application.
It’ll reuse the favicon of the website as icon for the application.
But be sure to check <code>Open as window</code> to create a standalone application:</p>
<p><figure>
  <a href="#img-2e3a81b0e456c9f0d87e97bc0d195e5c">
    <img src="/img/chrome-to-app/web2app-02-open-as-window.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-2e3a81b0e456c9f0d87e97bc0d195e5c">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/chrome-to-app/web2app-02-open-as-window.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Then you’ll be able to open the website as a standalone application:</p>
<p><figure>
  <a href="#img-7e2de5b268eb2ffd279624f802555e6f">
    <img src="/img/chrome-to-app/web2app-03-standalone-app.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-7e2de5b268eb2ffd279624f802555e6f">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/chrome-to-app/web2app-03-standalone-app.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I was curious if a similar feature existed with other browsers like Firefox.
For the little fox, the only thing I could find was the ability to open Firefox in kiosk mode,
in full-screen. But I wanted a window I could dimension however I wanted, not necessarily full-screen.
I hope that Firefox will add that capability at some point.
But for now, I’m happy to have this solution with Chrome!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Workflows patterns and best practices — Part 2</title><link>https://glaforge.dev/posts/2022/11/28/workflows-patterns-and-best-practices-part-2/</link><pubDate>Mon, 28 Nov 2022 14:12:05 +0100</pubDate><guid>https://glaforge.dev/posts/2022/11/28/workflows-patterns-and-best-practices-part-2/</guid><description>&lt;p>This is part 2 of a three-part series of posts, in which we summarize Workflows and service orchestration patterns. You can apply these patterns to better take advantage of Workflows and service orchestration on Google Cloud.&lt;/p>
&lt;p>In the &lt;a href="https://glaforge.dev/posts/2022/11/22/workflows-patterns-and-best-practices-part-1/">first post&lt;/a>, we introduced some general tips and tricks, as well as patterns for event-driven orchestrations, parallel steps, and connectors. This second post covers more advanced patterns.  &lt;/p>
&lt;p>Let&amp;rsquo;s dive in!&lt;/p>
&lt;h2 id="design-for-resiliency-with-retries-and-the-saga-pattern">Design for resiliency with retries and the saga pattern&lt;/h2>
&lt;p>It&amp;rsquo;s easy to put together a workflow that chains a series of services,  especially if you assume that those services will never fail. This is a common distributed systems fallacy, however, because of course a service will fail at some point. The workflow step calling that service will fail, and then the whole workflow will fail. This is not what you want to see in a resilient architecture. Thankfully, Workflows has building blocks to handle both transient and permanent service failures. &lt;/p></description><content:encoded>
<![CDATA[<p>This is part 2 of a three-part series of posts, in which we summarize Workflows and service orchestration patterns. You can apply these patterns to better take advantage of Workflows and service orchestration on Google Cloud.</p>
<p>In the <a href="https://glaforge.dev/posts/2022/11/22/workflows-patterns-and-best-practices-part-1/">first post</a>, we introduced some general tips and tricks, as well as patterns for event-driven orchestrations, parallel steps, and connectors. This second post covers more advanced patterns.  </p>
<p>Let&rsquo;s dive in!</p>
<h2 id="design-for-resiliency-with-retries-and-the-saga-pattern">Design for resiliency with retries and the saga pattern</h2>
<p>It&rsquo;s easy to put together a workflow that chains a series of services,  especially if you assume that those services will never fail. This is a common distributed systems fallacy, however, because of course a service will fail at some point. The workflow step calling that service will fail, and then the whole workflow will fail. This is not what you want to see in a resilient architecture. Thankfully, Workflows has building blocks to handle both transient and permanent service failures. </p>
<p>In our post on <a href="https://cloud.google.com/blog/topics/developers-practitioners/implementing-saga-pattern-workflows">Implementing the saga pattern in Workflows</a> (and its associated e-commerce <a href="https://github.com/GoogleCloudPlatform/workflows-demos/tree/master/retries-and-saga">sample</a>), we talked about how you can apply the saga pattern and take compensation steps to undo one or more previous steps with the <code>try/except</code> block for permanent service failures. </p>
<p><figure>
  <a href="#img-23af8078ad0a535c65bc3ac2369ea22f">
    <img src="https://storage.googleapis.com/gweb-cloudblog-publish/images/image3_2_1Fl6i9t.max-1400x1400.png"
      alt="https://storage.googleapis.com/gweb-cloudblog-publish/images/image3_2_1Fl6i9t.max-1400x1400.png"
       />
  </a>
  <figcaption>https://storage.googleapis.com/gweb-cloudblog-publish/images/image3_2_1Fl6i9t.max-1400x1400.png</figcaption>
</figure>
<div class="lightbox" id="img-23af8078ad0a535c65bc3ac2369ea22f">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://storage.googleapis.com/gweb-cloudblog-publish/images/image3_2_1Fl6i9t.max-1400x1400.png"
    alt="https://storage.googleapis.com/gweb-cloudblog-publish/images/image3_2_1Fl6i9t.max-1400x1400.png"
     />
  <div class="lightbox-caption">https://storage.googleapis.com/gweb-cloudblog-publish/images/image3_2_1Fl6i9t.max-1400x1400.png</div>
</div>
</p>
<p><em>Saga pattern implementation</em></p>
<p>We also showed how to handle transient service failures by adding the default HTTP retry policy with the <code>try/retry</code> block of Workflows. If you have non-idempotent steps, you need to adjust the retry policy to <code>default_retry_non_idempotent</code>. In most cases, you need a custom retry policy with a longer backoff, because the default one waits at most ~8 seconds.  When eventual consistency over a longer time period is more important than fast failure, consider a longer retry policy over several minutes or even hours with a large multiplier is much more likely to succeed during temporary outages. </p>
<p>Don&rsquo;t take network calls for granted. Make sure you design your orchestration with resiliency in mind using retries and the saga pattern.</p>
<h2 id="wait-for-http-and-event-callbacks-instead-of-polling">Wait for HTTP and event callbacks instead of polling</h2>
<p>Sometimes, your orchestration might need to wait for a long-running job or an asynchronous event in another system before it can continue with the rest of the steps. The workflow can ask for that input by polling an endpoint or a queue, but this requires complicated polling logic, wasted polling cycles and most likely higher latency. A better approach is to use Workflows <a href="https://cloud.google.com/workflows/docs/creating-callback-endpoints">callbacks</a> to wait for HTTP calls or events.  </p>
<p>In our <a href="https://cloud.google.com/blog/topics/developers-practitioners/introducing-workflows-callbacks">Introducing Workflows callbacks</a> post, we showed a workflow that waits for human input for automated machine learning translations. Similarly, the <a href="https://cloud.google.com/blog/topics/developers-practitioners/smarter-applications-document-ai-workflows-and-cloud-functions">Smarter applications with Document AI, Workflows and Cloud Functions</a> post shows a document processing workflow that waits for human approval of expense reports with a callback. </p>
<p><figure>
  <a href="#img-1c085c08ccf76872d7b2578d30b3891e">
    <img src="/img/workflows-patterns/architecture-diagram_6rwVYEP.max-1500x1500.png"
      alt="/img/workflows-patterns/architecture-diagram_6rwVYEP.max-1500x1500.png"
       />
  </a>
  <figcaption>/img/workflows-patterns/architecture-diagram_6rwVYEP.max-1500x1500.png</figcaption>
</figure>
<div class="lightbox" id="img-1c085c08ccf76872d7b2578d30b3891e">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-patterns/architecture-diagram_6rwVYEP.max-1500x1500.png"
    alt="/img/workflows-patterns/architecture-diagram_6rwVYEP.max-1500x1500.png"
     />
  <div class="lightbox-caption">/img/workflows-patterns/architecture-diagram_6rwVYEP.max-1500x1500.png</div>
</div>
</p>
<p><em>Smarter applications with Document AI, Workflows and Cloud Functions</em></p>
<p>While both of these posts are focused on waiting for <em>HTTP callbacks</em>, in <a href="https://medium.com/google-cloud/creating-workflows-that-pause-and-wait-for-events-4da201741f2a">Creating Workflows that pause and wait for events</a> post, we showed how a workflow can also wait for Pub/Sub and Cloud Storage events. You can even use Google Sheets as a quick and simple frontend for human approvals as we showed in the <a href="https://medium.com/google-cloud/workflows-that-pause-and-wait-for-human-approvals-from-google-sheets-53673ced2a81">Workflows that pause and wait for human approvals from Google Sheets</a> post. </p>
<p>When designing a workflow, consider waiting for HTTP calls and events, rather than polling.  </p>
<h2 id="orchestrate-long-running-batch-jobs">Orchestrate long-running batch jobs</h2>
<p>If you need to execute long-running jobs, Google Cloud has services such as <a href="https://cloud.google.com/batch">Batch</a> and <a href="https://cloud.google.com/run/docs/create-jobs">Cloud Run jobs</a> that can help. While these services are great for completing  long-running jobs on Compute Engine instances and containers, you still need to create and manage the Batch and Cloud Run job service. One pattern that works really well is to use Workflows to manage these services running batch jobs. </p>
<p>In the <a href="https://cloud.google.com/blog/topics/developers-practitioners/taking-screenshots-web-pages-cloud-run-jobs-workflows-and-eventarc">Taking screenshots of web pages with Cloud Run jobs, Workflows, and Eventarc</a> post, we showed how Cloud Run jobs take screenshots of web pages and Workflows creates and manages parallel Cloud Run jobs tasks. Similarly, in the <a href="https://github.com/GoogleCloudPlatform/batch-samples/tree/main/primegen">Batch - prime number generator</a> sample, we showed how to run prime number generator containers in parallel on Compute Engine instances using Google Batch. The lifecycle of the Batch job is managed by Workflows.</p>
<p><figure>
  <a href="#img-29976dfe939059b215a382f2a57b392f">
    <img src="/img/workflows-patterns/image1_2.max-2200x2200.png"
      alt="/img/workflows-patterns/image1_2.max-2200x2200.png"
       />
  </a>
  <figcaption>/img/workflows-patterns/image1_2.max-2200x2200.png</figcaption>
</figure>
<div class="lightbox" id="img-29976dfe939059b215a382f2a57b392f">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-patterns/image1_2.max-2200x2200.png"
    alt="/img/workflows-patterns/image1_2.max-2200x2200.png"
     />
  <div class="lightbox-caption">/img/workflows-patterns/image1_2.max-2200x2200.png</div>
</div>
</p>
<p><em>Take screenshots of webpages with Cloud Run jobs, Workflows and Eventarc</em></p>
<p>Use the right services for long-running batch jobs and use Workflows to manage their life cycles.</p>
<h2 id="treat-serverful-workloads-as-serverless-with-workflows">Treat serverful workloads as serverless with Workflows</h2>
<p>Sometimes, you really need a server due to some serverless limitation. For example, you might need to run on GPUs or execute a long-running process that lasts weeks or months. In those cases, Compute Engine can provide you with customized virtual machines (VM), but you&rsquo;re stuck with managing those VMs yourself. </p>
<p>In this kind of IT automation scenario, you can use Workflows to create VMs with the customizations you need, run the workloads for however long you need (Workflows executions can last up to one year), and return the result in the end.  This pattern enables you to use servers but manage them as if they were serverless services using Workflows.</p>
<p>In our <a href="https://cloud.google.com/blog/topics/developers-practitioners/long-running-containers-workflows-and-compute-engine">Long-running containers with Workflows and Compute Engine</a> post, we showed how to use Workflows to spin up a VM, start a prime number generator on the VM, run it for however long you want, and return the result.</p>
<p>Next time you need to spin up a VM, treat it like a serverless service with Workflows. </p>
<h2 id="run-command-line-tools-with-workflows-and-cloud-build">Run command-line tools with Workflows and Cloud Build</h2>
<p>We often use command-line tools such as <code>gcloud</code> to manage Google Cloud resources or <code>kubectl</code> to manage Kubernetes clusters. Wouldn&rsquo;t it be nice if we could call these tools from Workflows and orchestrate management of resources?</p>
<p>In the <a href="https://medium.com/google-cloud/executing-commands-gcloud-kubectl-from-workflows-ad6b85eaf39c">Executing commands (gcloud, kubectl) from Workflows</a> post, we showed how to use Cloud Build to run these tools and how to create and call that Cloud Build step from Workflows using the Cloud Build connector. </p>
<p>Keep in mind that this pattern is not limited to <code>gcloud</code> and <code>kubectl</code>. Any tool you can run in a container can potentially be a target for Workflows with the help of Cloud Build.</p>
<p>Integrate command-line tools into your workflows when needed by calling a Cloud Build step from Workflows.</p>
<hr />
<p>This second part series covered a lot of ground, but there&rsquo;s still more to cover! We&rsquo;ll wrap up the series in our <a href="https://glaforge.dev/posts/2022/12/06/workflows-patterns-and-best-practices-part-3/">third and final post</a>, which describes how to manage the lifecycle of workflow definitions and the benefits of using Firestore. Stay tuned! For questions or feedback, feel free to reach out to us on Twitter @<a href="https://twitter.com/meteatamel">meteatamel</a> and @<a href="https://twitter.com/glaforge">glaforge</a>.</p>
<p><a href="https://cloud.google.com/blog/topics/developers-practitioners/workflows-patterns-and-best-practices-part-1"></a></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Workflows patterns and best practices — Part 1</title><link>https://glaforge.dev/posts/2022/11/22/workflows-patterns-and-best-practices-part-1/</link><pubDate>Tue, 22 Nov 2022 14:11:45 +0100</pubDate><guid>https://glaforge.dev/posts/2022/11/22/workflows-patterns-and-best-practices-part-1/</guid><description>&lt;p>For the last couple of years, we&amp;rsquo;ve been using &lt;a href="https://cloud.google.com/workflows">Workflows&lt;/a>, Google Cloud&amp;rsquo;s service orchestrator, to bring order to our serverless microservices architectures. As we used and gained more experience with Workflows and service orchestration, we shared what he had learned in conference talks, blog posts, samples, and tutorials. Along the way, some common patterns and best practices emerged. &lt;/p>
&lt;p>To help you take better advantage of Workflows and service orchestration on Google Cloud, we&amp;rsquo;ve summarized these proven patterns and best practices in a three-part series of blog posts.&lt;/p></description><content:encoded>
<![CDATA[<p>For the last couple of years, we&rsquo;ve been using <a href="https://cloud.google.com/workflows">Workflows</a>, Google Cloud&rsquo;s service orchestrator, to bring order to our serverless microservices architectures. As we used and gained more experience with Workflows and service orchestration, we shared what he had learned in conference talks, blog posts, samples, and tutorials. Along the way, some common patterns and best practices emerged. </p>
<p>To help you take better advantage of Workflows and service orchestration on Google Cloud, we&rsquo;ve summarized these proven patterns and best practices in a three-part series of blog posts.</p>
<p>Let&rsquo;s get started with Part 1!</p>
<h2 id="make-a-conscious-choice-on-the-communication-style-upfront">Make a conscious choice on the communication style upfront</h2>
<p>Choosing a communication style is more of a task than a pattern, but it is an important one to complete before even considering service orchestration. </p>
<p>When you have multiple services, you need to decide how these services will communicate. The options are:</p>
<ul>
<li><strong>Direct</strong> service-to-service communication</li>
<li>Indirect <strong>event-driven</strong> communication (also known as choreography)</li>
<li>A central <strong>orchestrator</strong> (e.g. Workflows) directing the communication</li>
</ul>
<p>There&rsquo;s no right or wrong, only pros and cons. Direct service-to-service communication is easy to implement but creates tight coupling between services. Events enable loosely coupled services at the expense of harder monitoring and debugging when something goes wrong. An orchestrator, while less flexible, brings order to communication without the tight coupling of direct service-to-service communication and the chaos of events in choreographed architectures.</p>
<p>In our <a href="https://cloud.google.com/blog/topics/developers-practitioners/orchestrating-pic-daily-serverless-app-workflows">Orchestrating the Pic-a-Daily serverless app with Workflows</a> post, we explained how we transformed an event-driven application into an orchestrated application and the benefits of doing so. In <a href="https://cloud.google.com/blog/topics/developers-practitioners/choosing-right-orchestrator-google-cloud">Choosing the right orchestrator in Google Cloud</a>, we talked about which service is best suited for different orchestration needs (scheduled, service, and data). </p>
<p>As you design your architecture, make a conscious choice on the communication style with pros and cons in mind, and if you choose to use orchestration, be sure to use the right orchestrator for the task. </p>
<h2 id="keep-these-tips-and-tricks-for-workflows-in-mind">Keep these tips and tricks for Workflows in mind</h2>
<p>Once you decide to use Workflows for service orchestration, you&rsquo;ll realize that Workflows has its own strengths and idiosyncrasies. Here are some general tips and tricks that we found useful as we used Workflows:</p>
<ul>
<li><strong>Avoid hard-coding URLs</strong> for more portable workflows across environments.</li>
<li><strong>Use substeps</strong> to collect a common series of steps in one logical unit.</li>
<li><strong>Wrap string expressions</strong> to avoid parsing problems.</li>
<li><strong>Replace logic-less services with declarative API calls</strong> to avoid boilerplate code.</li>
<li><strong>Store what you need, free what you can</strong> to keep memory consumption under control.</li>
<li><strong>Use subworkflows and call external workflows</strong> to increase reuse.</li>
</ul>
<p>Check our <a href="https://glaforge.appspot.com/article/workflows-tips-n-tricks">post on Workflows tips and tricks</a> for a more detailed explanation of these points. </p>
<h3 id="consider-event-driven-orchestration">Consider event-driven orchestration</h3>
<p>The choice on the communication style is not all or nothing. You can and should combine different styles when it makes sense. For example, there is a common pattern where services that are closely related are managed by an orchestrator like Workflows but that orchestration is triggered by an event from a service like Eventarc. Similarly, we see architectures where the end of an orchestration is a Pub/Sub message to some other orchestration or service. </p>
<p>In our <a href="https://cloud.google.com/blog/topics/developers-practitioners/introducing-eventarc-triggers-workflows">Introducing Eventarc triggers for Workflows</a> post, we showed how easy it is to route events to Workflows using Eventarc. In the <a href="https://youtu.be/2SI12QE-2DU">Build an event-driven orchestration with Eventarc and Workflows</a> video and its associated <a href="https://codelabs.developers.google.com/codelabs/cloud-event-driven-orchestration?hl=en#8">codelab</a> and <a href="https://github.com/GoogleCloudPlatform/eventarc-samples/tree/main/processing-pipelines/image-v3">sample</a>, we showed how to design an image processing pipeline where the services are managed by Workflows but the orchestration is triggered in a loosely coupled way by a Cloud Storage event via Eventarc:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/2SI12QE-2DU?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>Mix communication styles to get the best of both worlds: use orchestration when you need tight coupling between services but loose coupling with other orchestrations via events.</p>
<h2 id="use-connectors-when-you-can">Use connectors when you can</h2>
<p>Workflows has a rich set of <a href="https://cloud.google.com/workflows/docs/connectors">connectors</a> to call other Google Cloud services. They handle the formatting of requests for you, providing methods and arguments so that you don&rsquo;t need to get deep into the gory details of a Google Cloud API. More importantly, connectors enable a workflow to transparently wait for long-running Cloud API calls. This saves you from the tedious work of iterating and waiting for calls to complete; connectors take care of this for you! </p>
<p>In our <a href="https://cloud.google.com/workflows/docs/connectors">Introducing new connectors for Workflows</a> post, we showed you how Compute Engine connector simplified creating and stopping a virtual machine. </p>
<p>Whenever you want to call a Google Cloud API from Workflows, check to see if there&rsquo;s a connector for it. You&rsquo;ll be glad that you did and you can always request a new connector <a href="https://docs.google.com/forms/d/e/1FAIpQLScdDRwFHcFuy28hvjGq0XCBMyiVuhHGq2c-7Gy_no1ZuqKAOg/viewform?resourcekey=0-0BRW58uzC8wnVntuOiM7AQ">here</a>, if there&rsquo;s no connector.</p>
<h2 id="parallelize-when-you-can">Parallelize when you can</h2>
<p>When we talk about Workflows, we often talk about steps executed one after another sequentially. While Workflows is fast enough to run steps sequentially with no noticeable delay, not all steps need to run sequentially. Independent steps can actually run in parallel, and in some cases this can provide a significant speed up for workflow executions.</p>
<p>In the <a href="https://cloud.google.com/blog/topics/developers-practitioners/introducing-parallel-steps-workflows-speed-up-workflow-executions-by-running-steps-concurrently">Introducing Parallel Steps for Workflows</a> post and its associated video, we showed how running BigQuery jobs from Workflows in parallel can speed up the workflow execution by five times. The more independent steps you have, the more you can run those steps in parallel and the faster your workflow execution will be, especially with long-running tasks like BigQuery jobs.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/C1Reg1u1MXY?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>Try to keep your steps independent and make sure to take advantage of parallel steps when you can.</p>
<hr />
<p>This initial list of patterns and tips will help you get started taking better advantage of Workflows. We cover more advanced patterns in <a href="https://glaforge.dev/posts/2022/11/28/workflows-patterns-and-best-practices-part-2/">part 2</a> of this series. For questions or feedback, feel free to reach out to us on Twitter @<a href="https://twitter.com/meteatamel">meteatamel</a> and @<a href="https://twitter.com/glaforge">glaforge</a>.</p>
<p><a href="https://cloud.google.com/blog/topics/developers-practitioners/orchestrating-pic-daily-serverless-app-workflows"></a></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>APIs, we have a Problem JSON</title><link>https://glaforge.dev/posts/2022/11/14/apis-we-have-a-problem-json/</link><pubDate>Mon, 14 Nov 2022 13:37:09 +0100</pubDate><guid>https://glaforge.dev/posts/2022/11/14/apis-we-have-a-problem-json/</guid><description>&lt;p>When designing a web API, not only do you have to think about the happy path when everything is alright,
but you also have to handle all the error cases: Is the payload received correct? Is there a typo in a field?
Do you need more context about the problem that occured?&lt;/p>
&lt;p>There’s only a limited set of status codes that can convey the kind of error you’re getting,
but sometimes you need to explain more clearly what the error is about.&lt;/p></description><content:encoded>
<![CDATA[<p>When designing a web API, not only do you have to think about the happy path when everything is alright,
but you also have to handle all the error cases: Is the payload received correct? Is there a typo in a field?
Do you need more context about the problem that occured?</p>
<p>There’s only a limited set of status codes that can convey the kind of error you’re getting,
but sometimes you need to explain more clearly what the error is about.</p>
<p>In the past, the APIs I was designing used to follow a common JSON structure for my error messages: a simple JSON object,
usually with a <code>message</code> field, and sometimes with extra info like a custom error <code>code</code>, or a <code>details</code> field that contained a longer explanation in plain English.
However, it was my own convention, and it’s not necessarily one that is used by others, or understood by tools that interact with my API.</p>
<p>So that’s why today, for reporting problems with my web APIs, I tend to use <strong>Problem JSON</strong>. This is actually an RFC (<a href="https://datatracker.ietf.org/doc/html/rfc7807">RFC-7807</a>)
whose title is “Problem Details for HTTP APIs”. Exactly what I needed, a specification for my error messages!</p>
<p>First of all, it’s a JSON content-type. Your API should specify the content-type with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-http" data-lang="http"><span style="display:flex;"><span><span style="">Content-Type: application/problem+json
</span></span></span></code></pre></div><p>Content-types that end with <code>+json</code> are basically treated as <code>application/json</code>.</p>
<p>Now, an example payload from the specification looks like:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-http" data-lang="http"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">HTTP</span><span style="color:#666">/</span><span style="color:#40a070">1.1</span> <span style="color:#40a070">403</span> <span style="color:#007020">Forbidden</span>
</span></span><span style="display:flex;"><span>Content-Type<span style="color:#666">:</span> application/problem+json
</span></span><span style="display:flex;"><span>Content-Language<span style="color:#666">:</span> en
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>   <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#4070a0">&#34;https://example.com/probs/out-of-credit&#34;</span>,
</span></span><span style="display:flex;"><span>   <span style="color:#062873;font-weight:bold">&#34;title&#34;</span>: <span style="color:#4070a0">&#34;You do not have enough credit.&#34;</span>,
</span></span><span style="display:flex;"><span>   <span style="color:#062873;font-weight:bold">&#34;detail&#34;</span>: <span style="color:#4070a0">&#34;Your current balance is 30, but that costs 50.&#34;</span>,
</span></span><span style="display:flex;"><span>   <span style="color:#062873;font-weight:bold">&#34;instance&#34;</span>: <span style="color:#4070a0">&#34;/account/12345/msgs/abc&#34;</span>,
</span></span><span style="display:flex;"><span>   <span style="color:#062873;font-weight:bold">&#34;balance&#34;</span>: <span style="color:#40a070">30</span>,
</span></span><span style="display:flex;"><span>   <span style="color:#062873;font-weight:bold">&#34;accounts&#34;</span>: [<span style="color:#4070a0">&#34;/account/12345&#34;</span>, <span style="color:#4070a0">&#34;/account/67890&#34;</span>]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>There are some standard fields like:</p>
<ul>
<li><strong>type</strong>: a URI reference that uniquely identifies the problem type</li>
<li><strong>title</strong>: a short readable error statement</li>
<li><strong>status</strong>: the original HTTP status code from the origin server</li>
<li><strong>detail</strong>: a longer text explanation of the issue</li>
<li><strong>instance</strong>: a URI that points to the resource that has issues</li>
</ul>
<p>Then, in the example above, you also have custom fields: <code>balance</code> and <code>accounts</code>, which are specific to your application,
and not part of the specification. Which means you can expand the Problem JSON payload to include details that are specific to your application.</p>
<p>Note: Although I’m only covering JSON APIs, the RFC also suggests an <code>application/problem+xml</code> alternative for the XML APIs.</p>
<h2 id="icing-on-the-cake-built-in-support-in-micronaut">Icing on the cake: built-in support in Micronaut</h2>
<p>My framework of choice these days for all my apps is <a href="https://micronaut.io/">Micronaut</a>, as it’s super fast and memory efficient.
And it’s only recently that I realized there was actually a <a href="https://micronaut-projects.github.io/micronaut-problem-json/2.5.2/guide/index.html">Micronaut extension for Problem JSON</a>!
So instead of returning a custom JSON payload manually, I can use the built-in integration.</p>
<p>Here’s an example from the Problem JSON Micronaut extension:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Controller</span>(<span style="color:#4070a0">&#34;/product&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">ProductController</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#555;font-weight:bold">@Get</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#555;font-weight:bold">@Status</span>(HttpStatus.<span style="color:#4070a0">OK</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">index</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">throw</span><span style="color:#bbb"> </span>Problem.<span style="color:#4070a0">builder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">withType</span>(URI.<span style="color:#4070a0">create</span>(<span style="color:#4070a0">&#34;https://example.org/out-of-stock&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">withTitle</span>(<span style="color:#4070a0">&#34;Out of Stock&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">withStatus</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>HttpStatusType(HttpStatus.<span style="color:#4070a0">BAD_REQUEST</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">withDetail</span>(<span style="color:#4070a0">&#34;Item B00027Y5QG is no longer available&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">with</span>(<span style="color:#4070a0">&#34;product&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;B00027Y5QG&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>.<span style="color:#4070a0">build</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Which will return a JSON error as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;status&#34;</span>: <span style="color:#40a070">400</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;title&#34;</span>: <span style="color:#4070a0">&#34;Out of Stock&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;detail&#34;</span>: <span style="color:#4070a0">&#34;Item B00027Y5QG is no longer available&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#4070a0">&#34;https://example.org/out-of-stock&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;parameters&#34;</span>: {<span style="color:#062873;font-weight:bold">&#34;product&#34;</span>: <span style="color:#4070a0">&#34;B00027Y5QG&#34;</span>}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Now, I’m happy that I can use some official standard for giving more details about the errors returned by my APIs!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Workflows Tips and Tricks</title><link>https://glaforge.dev/posts/2022/11/04/workflows-tips-and-tricks/</link><pubDate>Fri, 04 Nov 2022 14:15:43 +0100</pubDate><guid>https://glaforge.dev/posts/2022/11/04/workflows-tips-and-tricks/</guid><description>&lt;p>Here are some general tips and tricks that we found useful as we used Google Cloud &lt;a href="https://cloud.google.com/workflows">Workflows&lt;/a>:&lt;/p>
&lt;h2 id="avoid-hard-coding-urls">Avoid hard-coding URLs&lt;/h2>
&lt;p>Since Workflows is all about calling APIs and service URLs, it&amp;rsquo;s important to have some clean way to handle those URLs.
You can hard-code them in your workflow definition, but the problem is that your workflow can become harder to maintain.
In particular, what happens when you work with multiple environments?
You have to duplicate your YAML definitions and use different URLs for the prod vs staging vs dev environments.
It is error-prone and quickly becomes painful to make modifications to essentially the same workflow in multiple files.
To avoid hard-coding those URLs, there are a few approaches.&lt;/p></description><content:encoded>
<![CDATA[<p>Here are some general tips and tricks that we found useful as we used Google Cloud <a href="https://cloud.google.com/workflows">Workflows</a>:</p>
<h2 id="avoid-hard-coding-urls">Avoid hard-coding URLs</h2>
<p>Since Workflows is all about calling APIs and service URLs, it&rsquo;s important to have some clean way to handle those URLs.
You can hard-code them in your workflow definition, but the problem is that your workflow can become harder to maintain.
In particular, what happens when you work with multiple environments?
You have to duplicate your YAML definitions and use different URLs for the prod vs staging vs dev environments.
It is error-prone and quickly becomes painful to make modifications to essentially the same workflow in multiple files.
To avoid hard-coding those URLs, there are a few approaches.</p>
<p>The first one is to externalize those URLs, and
<a href="https://github.com/GoogleCloudPlatform/workflows-demos/tree/master/multi-env-deployment#option-1-use-urls-as-runtime-arguments">pass them as workflow execution arguments</a>.
This is great for workflow executions that are launched via the CLI, via the various client libraries, or the REST &amp; gRPC APIs.
However, there&rsquo;s a limitation to this first approach, in the case of event-triggered workflows, where the invoker is Eventarc.
In that case, that&rsquo;s Eventarc that decides which arguments to pass (ie. the event payload).
There&rsquo;s no way to pass extra arguments in that case.</p>
<p>A safer approach is then to use some placeholder replacement techniques.
Just use a tool that replaces some specific string tokens in your definition file, before deploying that updated definition.
We explored that approach using some
<a href="https://github.com/GoogleCloudPlatform/workflows-demos/tree/master/multi-env-deployment#option-2-use-cloud-build-to-deploy-multiple-versions">Cloud Build steps that do some string replacement</a>.
You still have one single workflow definition file, but you deploy variants for the different environments.
If you&rsquo;re using Terraform for provisioning your infrastructure, we&rsquo;ve got you covered, you can also employ a
<a href="https://github.com/GoogleCloudPlatform/workflows-demos/tree/master/multi-env-deployment#option-3-use-terraform-to-deploy-multiple-versions">similar technique with Terraform</a>.</p>
<p>There are also other possible approaches, like taking advantage of Secret Manager and the dedicated
<a href="https://cloud.google.com/workflows/docs/reference/googleapis/secretmanager/Overview">workflow connector</a>,
to store those URLs, and retrieve them. Or you can also
<a href="https://github.com/GoogleCloudPlatform/workflows-demos/tree/master/gcs-read-write-json#load-environment-specific-variables-from-a-json-file-in-gcs">read some JSON file in a cloud storage bucket</a>,
within which you would store those environment specific details.</p>
<h2 id="take-advantage-of-sub-steps">Take advantage of sub-steps</h2>
<p>Apart from branching or looping, defining your steps is a pretty sequential process. One step happens after another.
Steps are their own atomic operation.
However, often, some steps really go hand-in-hand, like making an API call, logging its outcome, retrieving and assigning parts of the payload into some variables.
You can actually regroup common steps into substeps. This becomes handy when you are branching from a set of steps to another set of steps, without having to point at the right atomic step.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">main</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">params</span>:<span style="color:#bbb">  </span>[input]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">callWikipedia</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">checkSearchTermInInput</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">switch</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>- <span style="color:#062873;font-weight:bold">condition</span>:<span style="color:#bbb">  </span>${&#34;searchTerm&#34;  in  input}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                  </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>- <span style="color:#062873;font-weight:bold">searchTerm</span>:<span style="color:#bbb">  </span>${input.searchTerm}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                  </span><span style="color:#062873;font-weight:bold">next</span>:<span style="color:#bbb">  </span>readWikipedia<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">getCurrentTime</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>http.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb"> </span>...<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb">  </span>currentDateTime<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">setFromCallResult</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>- <span style="color:#062873;font-weight:bold">searchTerm</span>:<span style="color:#bbb">  </span>${currentDateTime.body.dayOfTheWeek}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">readWikipedia</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>http.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb">  </span>https://en.wikipedia.org/w/api.php<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">query</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span><span style="color:#062873;font-weight:bold">action</span>:<span style="color:#bbb">  </span>opensearch<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span><span style="color:#062873;font-weight:bold">search</span>:<span style="color:#bbb">  </span>${searchTerm}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb">  </span>wikiResult<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">returnOutput</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span>${wikiResult.body[1]}<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="wrap-expressions">Wrap expressions</h2>
<p>The dollar/curly brace <code>${}</code> expressions are not part of the YAML specification, so what you put inside sometimes doesn&rsquo;t play well with YAML&rsquo;s expectations.
For example, putting a colon inside a string inside an expression can be problematic, as the YAML parser believes the colon is the end of the YAML key, and the start of the right-hand-side.
So to be safe, you can actually wrap your expressions within quotes, like: <code>'${...}'</code></p>
<p>Expressions can span several lines, as well as the strings within that expression.
That&rsquo;s handy for SQL queries for BigQuery, like in our <a href="https://github.com/GoogleCloudPlatform/workflows-demos/tree/master/bigquery-parallel">example</a>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">query</span>:<span style="color:#bbb">  </span>${<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;SELECT TITLE, SUM(views)
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    FROM `bigquery-samples.wikipedia_pageviews.&#34;</span><span style="color:#bbb">  </span>+  table  +  &#34;`<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>WHERE LENGTH(TITLE) &gt; 10<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>GROUP BY TITLE<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>ORDER BY SUM(VIEWS) DESC<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>LIMIT 100&#34;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="replace-logic-less-services-with-declarative-api-calls">Replace logic-less services with declarative API calls</h2>
<p>In our <a href="https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop/">serverless workshop</a>, in lab 1,
we had a <a href="https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop/blob/master/functions/image-analysis/nodejs/index.js#L19">function service</a>
that was making a call to the Cloud Vision API, checking a boolean attribute, then writing the result in Firestore.
But the Vision API can be called declaratively from Workflows.
The boolean check can be done with a switch conditional expression, and even writing to Firestore can be done via a declarative API call.
When rewriting our application in <a href="https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop/blob/master/workflows/workflows.yaml#L33">lab 6</a>
to use the orchestrated approach, we moved those logic-less calls into declarative API calls.</p>
<p>There are times where Workflows lack some built-in function that you would need, so you have no choice but fork into a function to do the job.
But when you have pretty logic-less code that just makes some API calls, you&rsquo;d better just write this declaratively using Workflows syntax.</p>
<p>It doesn&rsquo;t mean that everything, or as much as possible, should be done declaratively in a Workflow either.
Workflows is not a hammer, and it&rsquo;s definitely not a programming language.
So when there&rsquo;s real logic, you definitely need to call some service that represents that business logic.</p>
<h2 id="store-what-you-need-free-what-you-can">Store what you need, free what you can</h2>
<p>Workflows keeps on granting more memory to workflow executions, but there are times, with big API response payloads, where you&rsquo;d be happy to have even more memory.
That&rsquo;s when sane memory management can be a good thing to do.
You can be selective in what you store into variables: don&rsquo;t store too much, but store just the right payload part you really need.
Once you know you won&rsquo;t need the content of one of your variables, you can also reassign that variable to null, that should also free that memory.
Also, in the first place, if the APIs allow you to filter the result more aggressively, you should also do that.
Last but not least, if you&rsquo;re calling a service that returns a gigantic payload that can&rsquo;t fit in Workflows memory,
you could always delegate that call to your own function that would take care of making the call on your behalf, and returning to you just the parts you&rsquo;re really interested in.</p>
<p>Don&rsquo;t forget to check the documentation on <a href="https://cloud.google.com/workflows/quotas">quotas and limits</a> to know more about what&rsquo;s possible.</p>
<h2 id="take-advantage-of-sub-workflows-and-the-ability-to-call-external-workflows">Take advantage of sub-workflows and the ability to call external workflows</h2>
<p>In your workflows, sometimes there are some steps that you might need to repeat.
That&rsquo;s when <a href="https://cloud.google.com/workflows/docs/reference/syntax/subworkflows">subworkflows</a> become handy.
Sub-workflows are like sub-routines, procedures or methods.
They are a way to make a set of steps reusable in several places of your workflow, potentially parameterized with different arguments.
The sole downside maybe is that subworkflows are just local to your workflow definition, so they can&rsquo;t be reused in other workflows.
In that case, you could actually create a dedicated reusable workflow, because you can also
<a href="https://cloud.google.com/workflows/docs/reference/googleapis/workflowexecutions/Overview">call workflows from other workflows</a>! The workflows connector for workflows is there to help.</p>
<h2 id="summary">Summary</h2>
<p>We&rsquo;ve covered a few tips and tricks, and we&rsquo;ve reviewed some useful advice on how to make the best use of Workflows.
There are certainly others we&rsquo;re forgetting about.
So feel free to share them with <a href="https://twitter.com/meteatamel">@meteatamel</a> and <a href="https://twitter.com/glaforge">@glaforge</a> over Twitter.</p>
<p>And don&rsquo;t forget to double check what&rsquo;s in the Workflows <a href="https://cloud.google.com/workflows/docs">documentation</a>.
In particular, have a look at the built-in functions of the <a href="https://cloud.google.com/workflows/docs/reference/stdlib/overview">standard library</a>,
at the <a href="https://cloud.google.com/workflows/docs/reference/googleapis">list of connectors</a> that you can use,
and perhaps even print the <a href="https://cloud.google.com/workflows/docs/reference/syntax/syntax-cheat-sheet">syntax cheat sheet</a>!</p>
<p>Lastly, check out all the <a href="https://cloud.google.com/workflows/docs/samples">samples</a> in the documentation portal,
and all the <a href="https://github.com/GoogleCloudPlatform/workflows-demos">workflow demos</a> Mete and I have built and open sourced over time.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Retrieve YouTube views count with youtubeDL, JQ, and a Docker container</title><link>https://glaforge.dev/posts/2022/10/26/retrieve-youtube-views-count-with-youtubedl-jq-and-a-docker-container/</link><pubDate>Wed, 26 Oct 2022 14:53:39 +0100</pubDate><guid>https://glaforge.dev/posts/2022/10/26/retrieve-youtube-views-count-with-youtubedl-jq-and-a-docker-container/</guid><description>&lt;p>I wanted to track the number of views, and also likes, of some YouTube videos I was featured in.
For example, when I present a talk at a conference, often the video becomes available at a later time, and I&amp;rsquo;m not the owner of the channel or video.
At first, I wanted to use the &lt;a href="https://developers.google.com/youtube/v3">YouTube Data API&lt;/a>,
but I had the impression that I could only see the stats of videos or channels I own,
however I think I might be wrong, and should probably revisit this approach later on.&lt;/p></description><content:encoded>
<![CDATA[<p>I wanted to track the number of views, and also likes, of some YouTube videos I was featured in.
For example, when I present a talk at a conference, often the video becomes available at a later time, and I&rsquo;m not the owner of the channel or video.
At first, I wanted to use the <a href="https://developers.google.com/youtube/v3">YouTube Data API</a>,
but I had the impression that I could only see the stats of videos or channels I own,
however I think I might be wrong, and should probably revisit this approach later on.</p>
<p>My first intuition was to just scrape the web page of the video, but it&rsquo;s a gobbledygook of JavaScript,
and I couldn&rsquo;t really find an easy way to consistently get the numbers in that sea of compressed JavaScript.
That&rsquo;s when I remembered about the <a href="https://youtube-dl.org/">youtube-dl project</a>.
Some people think of this project as a way to download videos to watch offline, but it&rsquo;s also a useful tool that offers lots of metadata about the videos.
You can actually even use the project without downloading videos at all, but just fetching the metadata.</p>
<p>For example, if I want to get the video metadata, without downloading, I can launch the following command, after having installed the tool locally:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>youtube-dl -j -s https://www.youtube.com/watch?v<span style="color:#666">=</span>xJi6pldZnsw
</span></span></code></pre></div><p>The <code>-s</code> flag is equivalent to <code>--simulate</code> which doesn&rsquo;t download anything on disk.</p>
<p>And the <code>-j</code> flag is the short version of <code>--dump-json</code> which returns a big JSON file with lots of metadata, including the view count,
but also things like links to transcripts in various languages, chapters, creator, duration, episode number, and so on and so forth.</p>
<p>Now, I&rsquo;m only interested in view counts, likes, dislikes.
So I&rsquo;m using <a href="https://stedolan.github.io/jq/">jq</a> to filter the big JSON payload, and create a resulting JSON document with just the fields I want.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>jq <span style="color:#4070a0">&#39;{&#34;id&#34;:.id,&#34;title&#34;:.title,&#34;views&#34;:.view_count,&#34;likes&#34;:(.like_count // 0), &#34;dislikes&#34;:(.dislike_count // 0)}&#39;</span>
</span></span></code></pre></div><p>This long command is creating a JSON structure as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span> <span style="color:#062873;font-weight:bold">&#34;id&#34;</span>: <span style="color:#4070a0">&#34;xJi6pldZnsw&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#062873;font-weight:bold">&#34;title&#34;</span>: <span style="color:#4070a0">&#34;Reuse old smartphones to monitor 3D prints, with WebRTC, WebSockets and Serverless by G. Laforge&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#062873;font-weight:bold">&#34;views&#34;</span>: <span style="color:#40a070">172</span>,
</span></span><span style="display:flex;"><span> <span style="color:#062873;font-weight:bold">&#34;likes&#34;</span>: <span style="color:#40a070">6</span>,
</span></span><span style="display:flex;"><span> <span style="color:#062873;font-weight:bold">&#34;dislikes&#34;</span>: <span style="color:#40a070">0</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The <code>.id</code>, <code>.title</code>, <code>.view_count</code>, etc, are searching for that particular key in the big JSON documentation.
The <code>// 0</code> notation is to avoid null values and return 0 if there&rsquo;s no key or if the value associated with the key is null.
So I always get a number &mdash; although I noticed that sometimes, the likes are not always properly accounted for, but I haven&rsquo;t figured out why.</p>
<p>So far so good&hellip; but if you pass a URL of a video with a playlist, or if you pass a playlist URL, it will fetch all the metadata for all the videos.
This is actually useful: you can even create your own playlists for the videos you want to track.
There&rsquo;s one odd thing happening though when using youtube-dl with such URLs: it will output a JSON document per line for each video.
It&rsquo;s not returning an array of those documents. So I found a nice trick with jq to always put the results within an array, whether you pass a URL for a single video, or a video with a playlist:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>​​jq -n <span style="color:#4070a0">&#39;[inputs]&#39;</span>
</span></span></code></pre></div><p>So I&rsquo;m piping the <code>youtube-dl</code> command, the first and second <code>jq</code> commands.</p>
<p>Rather than installing those tools locally, I decided to containerize my magic commands.</p>
<p>Let me first show you the whole <code>Dockerfile</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Dockerfile" data-lang="Dockerfile"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#4070a0"> ubuntu:latest</span><span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">RUN</span> apt-get update <span style="color:#666">&amp;&amp;</span> apt-get -y install wget<span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span>    <span style="color:#666">&amp;&amp;</span> wget https://yt-dl.org/latest/youtube-dl -O /usr/local/bin/youtube-dl<span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span>    <span style="color:#666">&amp;&amp;</span> chmod a+rx /usr/local/bin/youtube-dl<span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span>    <span style="color:#666">&amp;&amp;</span> apt-get -y install python3-pip jq <span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span>    <span style="color:#666">&amp;&amp;</span> pip3 install --upgrade youtube-dl<span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">COPY</span> ./launch-yt-dl.sh<span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">RUN</span> chmod +x /launch-yt-dl.sh<span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">ENTRYPOINT</span> [<span style="color:#4070a0">&#34;./launch-yt-dl.sh&#34;</span>]<span style="">
</span></span></span></code></pre></div><p>And also this bash script mentioned in the Dockerfile:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007020">#!/bin/bash\
</span></span></span><span style="display:flex;"><span><span style="color:#007020"></span>youtube-dl -j -s -- <span style="color:#4070a0">&#34;</span><span style="color:#bb60d5">$@</span><span style="color:#4070a0">&#34;</span> | jq <span style="color:#4070a0">&#39;{&#34;id&#34;:.id,&#34;title&#34;:.title,&#34;views&#34;:.view_count,&#34;likes&#34;:(.like_count // 0), &#34;dislikes&#34;:(.dislike_count // 0)}&#39;</span> | jq -n <span style="color:#4070a0">&#39;[inputs]&#39;</span>
</span></span></code></pre></div><p>I went with the latest ubuntu image. I ran some apt-get commands to install wget to download the latest youtube-dl release, Python 3&rsquo;s pip to upgrade youtube-dl.
There&rsquo;s no recent apt module for youtube-dl, hence why we have those steps together.</p>
<p>What&rsquo;s more interesting is why I don&rsquo;t have the youtube-dl and jq commands in the Dockerfile directly, but instead in a dedicated bash script.
Initially I had an <code>ENTRYPOINT</code> that pointed at youtube-dl, so that arguments passed to the docker run command would be passed as arguments of that entrypoint.
However, after those commands, I still have to pipe with my jq commands. And I couldn&rsquo;t find how to do so with <code>ENTRYPOINT</code> and <code>CMD</code>.
When raising the problem on <a href="https://twitter.com/glaforge/status/1584800385256280064">twitter</a>,
my friends <a href="https://twitter.com/glours/status/1584810960136683521">Guillaume Lours</a> and <a href="https://twitter.com/cfurmaniak/status/1584845647647506432">Christophe Furmaniak</a> pointed me in the right direction with this idea of passing through a script.</p>
<p>So I use the <code>$@</code> bash shortcut, which expands as arguments <code>$1 $2 $3</code>, etc. in case there are several videos passed as arguments.
I have the jq pipes after that shortcut.
But for my <code>ENTRYPOINT</code>, it&rsquo;s fine, the args are passed directly to it, and it&rsquo;s that intermediary script that weaves the args in my longer command.</p>
<p>Next, I just need to build my Docker container:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker build . -t yt-video-stat
</span></span></code></pre></div><p>And then run it:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker run --rm -it yt-video-stat <span style="color:#4070a0">&#34;https://www.youtube.com/watch?v=xJi6pldZnsw&#34;</span>
</span></span></code></pre></div><p>And voila, I have the stats for the YouTube videos I&rsquo;m interested in!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Build and deploy Java 17 apps on Cloud Run with Cloud Native Buildpacks on Temurin</title><link>https://glaforge.dev/posts/2022/10/24/build-deploy-java-17-apps-on-cloud-run-with-cloud-native-buildpacks-on-temurin/</link><pubDate>Mon, 24 Oct 2022 11:33:25 +0100</pubDate><guid>https://glaforge.dev/posts/2022/10/24/build-deploy-java-17-apps-on-cloud-run-with-cloud-native-buildpacks-on-temurin/</guid><description>&lt;p>In this article, let&amp;rsquo;s revisit the topic of deploying Java apps on &lt;a href="https://cloud.run/">Cloud Run&lt;/a>.
In particular, I&amp;rsquo;ll deploy a &lt;a href="https://micronaut.io/">Micronaut&lt;/a> app, written with &lt;a href="https://jdk.java.net/17/">Java 17&lt;/a>, and built with &lt;a href="https://gradle.org/">Gradle&lt;/a>.&lt;/p>
&lt;h2 id="with-a-custom-dockerfile">With a custom Dockerfile&lt;/h2>
&lt;p>On Cloud Run, you deploy containerised applications, so you have to decide the way you want to build a container for your application.
In a &lt;a href="https://glaforge.dev/posts/2020/03/24/start-the-fun-with-java-14-and-micronaut-inside-serverless-containers-on-cloud-run/">previous article&lt;/a>,
I showed an example of using your own Dockerfile, which would look as follows with an OpenJDK 17, and enabling preview features of the language:&lt;/p></description><content:encoded>
<![CDATA[<p>In this article, let&rsquo;s revisit the topic of deploying Java apps on <a href="https://cloud.run/">Cloud Run</a>.
In particular, I&rsquo;ll deploy a <a href="https://micronaut.io/">Micronaut</a> app, written with <a href="https://jdk.java.net/17/">Java 17</a>, and built with <a href="https://gradle.org/">Gradle</a>.</p>
<h2 id="with-a-custom-dockerfile">With a custom Dockerfile</h2>
<p>On Cloud Run, you deploy containerised applications, so you have to decide the way you want to build a container for your application.
In a <a href="https://glaforge.dev/posts/2020/03/24/start-the-fun-with-java-14-and-micronaut-inside-serverless-containers-on-cloud-run/">previous article</a>,
I showed an example of using your own Dockerfile, which would look as follows with an OpenJDK 17, and enabling preview features of the language:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Dockerfile" data-lang="Dockerfile"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#4070a0"> openjdk:17</span><span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">WORKDIR</span><span style="color:#4070a0"> /app</span><span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">COPY</span> ./ ./<span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">RUN</span> ./gradlew shadowJar<span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">EXPOSE</span><span style="color:#4070a0">  8080</span><span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">CMD</span> [<span style="color:#4070a0">&#34;java&#34;</span>, <span style="color:#4070a0">&#34;--enable-preview&#34;</span>, <span style="color:#4070a0">&#34;-jar&#34;</span>, <span style="color:#4070a0">&#34;build/libs/app-0.1-all.jar&#34;</span>]<span style="">
</span></span></span></code></pre></div><p>To further improve on that Dockerfile, you could use a multistage Docker build to first build the app in one step with Gradle, and then run it in a second step.
Also you might want to parameterize the command as the JAR file name is hard-coded.</p>
<p>To build the image, you can build it locally with Docker, and then push it to Container Registry, and then deploy it:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># gcloud auth configure-docker</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># gcloud components install docker-credential-gcr</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>docker build . --tag gcr.io/YOUR_PROJECT_ID/IMAGE_NAME
</span></span><span style="display:flex;"><span>docker push gcr.io/YOUR_PROJECT_ID/IMAGE_NAME
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>gcloud run deploy weekend-service <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --image gcr.io/YOUR_PROJECT_ID/IMAGE_NAME
</span></span></code></pre></div><p>Instead of building locally with Docker, you could also let <a href="https://cloud.google.com/build">Cloud Build</a> do it for you:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud builds submit . --tag gcr.io/YOUR_PROJECT_ID/SERVICE_NAME
</span></span></code></pre></div><h2 id="with-jib">With JIB</h2>
<p>Instead of messing around with Dockerfiles, you can also let <a href="https://github.com/GoogleContainerTools/jib">JIB</a> create the container for you,
like I wrote in <a href="https://glaforge.dev/posts/2020/08/04/running-micronaut-serverlessly-on-google-cloud-platform/">another article</a>.
You configure Gradle to use the JIB plugin:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>plugins <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">...</span>
</span></span><span style="display:flex;"><span>id <span style="color:#4070a0">&#34;com.google.cloud.tools.jib&#34;</span> version <span style="color:#4070a0">&#34;2.8.0&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">...</span>
</span></span><span style="display:flex;"><span>tasks <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    jib <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        from <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>            image <span style="color:#666">=</span> <span style="color:#4070a0">&#34;gcr.io/distroless/java17-debian11&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>        to <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>            image <span style="color:#666">=</span> <span style="color:#4070a0">&#34;gcr.io/YOUR_PROJECT_ID/SERVICE_NAME&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>You specify the version of the plugin, but you also indicate that you want to use Java 17 by choosing a base image with that same version.
Be sure to change the placeholders for your project ID and service name.
Feel free to lookup the <a href="https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin">documentation</a> about the JIB Gradle plugin.
You can then let Gradle build the container with ./gradlew jib, or with ./gradlew jibDockerBuild if you want to use your local Docker daemon.</p>
<h2 id="with-cloud-native-buildpacks">With Cloud Native Buildpacks</h2>
<p>Now that we covered the other approaches, let&rsquo;s zoom in on using <a href="https://buildpacks.io/">Cloud Native Buildpacks</a> instead, in particular,
the <a href="https://github.com/GoogleCloudPlatform/buildpacks">Google Cloud Native Buildpacks</a>.
With buildpacks, you don&rsquo;t have to bother with Dockerfiles or with building the container before deploying the service.
You let Cloud Run use buildpacks to build, containerize, and deploy your application from sources.</p>
<p>Out of the box, the buildpack actually targets Java 8 or Java 11.
But I&rsquo;m interested in running the latest LTS version of Java, with Java 17, to take advantage of some preview features like records, sealed classes, switch expressions, etc.</p>
<p>In my Gradle build, I specify that I&rsquo;m using Java 17, but also enable preview features:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>java <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    toolchain <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        languageVersion<span style="color:#666">.</span><span style="color:#4070a0">set</span><span style="color:#666">(</span>JavaLanguageVersion<span style="color:#666">.</span><span style="color:#4070a0">of</span><span style="color:#666">(</span><span style="color:#40a070">17</span><span style="color:#666">))</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Like in Cédric Champeaus&rsquo;s <a href="https://melix.github.io/blog/2020/06/java-feature-previews-gradle.html">blog post</a>, to enable preview features,
you should also tell Gradle you want to enable them for compilation, test, and execution tasks:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>tasks<span style="color:#666">.</span><span style="color:#4070a0">withType</span><span style="color:#666">(</span>JavaCompile<span style="color:#666">).</span><span style="color:#4070a0">configureEach</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span> options<span style="color:#666">.</span><span style="color:#4070a0">compilerArgs</span><span style="color:#666">.</span><span style="color:#4070a0">add</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;--enable-preview&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>tasks<span style="color:#666">.</span><span style="color:#4070a0">withType</span><span style="color:#666">(</span>Test<span style="color:#666">).</span><span style="color:#4070a0">configureEach</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    useJUnitPlatform<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    jvmArgs<span style="color:#666">(</span><span style="color:#4070a0">&#34;--enable-preview&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>tasks<span style="color:#666">.</span><span style="color:#4070a0">withType</span><span style="color:#666">(</span>JavaExec<span style="color:#666">).</span><span style="color:#4070a0">configureEach</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    jvmArgs<span style="color:#666">(</span><span style="color:#4070a0">&#34;--enable-preview&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>So far so good, but as I said, the default native buildpack isn&rsquo;t using Java 17, and I want to specify that I use preview features.
So when I tried to deploy my Cloud Run app from sources with the buildpack, simply by running the gcloud deploy command, I would get an error.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud beta run deploy SERVICE_NAME
</span></span></code></pre></div><p>To circumvent this problem, I had to add a configuration file, to instruct the buildpack to use Java 17. I created a project.toml file at the root of my project:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span>[[build.env]]
</span></span><span style="display:flex;"><span>name = <span style="color:#4070a0">&#34;GOOGLE_RUNTIME_VERSION&#34;</span>
</span></span><span style="display:flex;"><span>value = <span style="color:#4070a0">&#34;17&#34;</span>
</span></span><span style="display:flex;"><span>[[build.env]]
</span></span><span style="display:flex;"><span>name = <span style="color:#4070a0">&#34;GOOGLE_ENTRYPOINT&#34;</span>
</span></span><span style="display:flex;"><span>value = <span style="color:#4070a0">&#34;java --enable-preview -jar /workspace/build/libs/app-0.1-all.jar&#34;</span>
</span></span></code></pre></div><p>I specify that the runtime version must use Java 17. But I also add the &ndash;enable-preview flag to enable the preview features at runtime.</p>
<h2 id="adoptium-temuring-openjdk-17">Adoptium Temuring OpenJDK 17</h2>
<p>The icing on the cake is that the build is using <a href="https://adoptium.net/">Adoptium</a>&rsquo;s <a href="https://adoptium.net/temurin/releases/">Temurin</a> build of OpenJDK 17,
as we recently <a href="https://blog.adoptium.net/2022/10/adoptium-welcomes-google/">announced</a>!
If you look at the build logs in Cloud Build, you should see some output mentioning it, like:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span> <span style="color:#062873;font-weight:bold">&#34;link&#34;</span>: <span style="color:#4070a0">&#34;https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.4.1%2B1/OpenJDK17U-jdk-sources_17.0.4.1_1.tar.gz&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#062873;font-weight:bold">&#34;name&#34;</span>: <span style="color:#4070a0">&#34;OpenJDK17U-jdk-sources_17.0.4.1_1.tar.gz&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#062873;font-weight:bold">&#34;size&#34;</span>: <span style="color:#40a070">105784017</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Way to go! Java 17 Micronaut app, deployed on Temurin on Cloud Run thanks to cloud native buildpacks! I win at buzzword bingo 🙂</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Choreography vs orchestration in microservices and best practices</title><link>https://glaforge.dev/talks/2022/10/20/choreography-vs-orchestration-in-microservices-and-best-practices/</link><pubDate>Thu, 20 Oct 2022 21:56:33 +0100</pubDate><guid>https://glaforge.dev/talks/2022/10/20/choreography-vs-orchestration-in-microservices-and-best-practices/</guid><description>&lt;p>We went from a single monolith to a set of microservices that are small, lightweight, and easy to implement. Microservices enable reusability, make it easier to change and scale apps on demand but they also introduce new problems. How do microservices interact with each other toward a common goal? How do you figure out what went wrong when a business process composed of several microservices fails? Should there be a central orchestrator controlling all interactions between services or should each service work independently, in a loosely coupled way, and only interact through shared events? In this talk, we’ll explore the Choreography vs Orchestration question and see demos of some of the tools that can help. And we&amp;rsquo;ll explore some best practices and patterns to apply when adopting an orchestration approach.&lt;/p></description><content:encoded>
<![CDATA[<p>We went from a single monolith to a set of microservices that are small, lightweight, and easy to implement. Microservices enable reusability, make it easier to change and scale apps on demand but they also introduce new problems. How do microservices interact with each other toward a common goal? How do you figure out what went wrong when a business process composed of several microservices fails? Should there be a central orchestrator controlling all interactions between services or should each service work independently, in a loosely coupled way, and only interact through shared events? In this talk, we’ll explore the Choreography vs Orchestration question and see demos of some of the tools that can help. And we&rsquo;ll explore some best practices and patterns to apply when adopting an orchestration approach.</p>
<script async class="speakerdeck-embed" data-id="328a826a55f447f7907db099c0aa03ab" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Reuse old smartphones to monitor 3D prints with WebRTC WebSockets and serverless</title><link>https://glaforge.dev/talks/2022/10/13/reuse-old-smartphones-to-monitor-3d-prints-with-webrtc-websockets-and-serverless/</link><pubDate>Thu, 13 Oct 2022 13:59:44 +0100</pubDate><guid>https://glaforge.dev/talks/2022/10/13/reuse-old-smartphones-to-monitor-3d-prints-with-webrtc-websockets-and-serverless/</guid><description>&lt;p>Reuse old smartphones to monitor 3D prints, with WebRTC, WebSockets and Serverless
Monitoring my 3D prints in my basement means climbing lots of stairs back and forth! So here’s my story about how I reused an old smartphone to check the status of my prints. I built a small web app that uses WebRTC to exchange video streams between my broadcasting smartphone and viewers, with WebSockets for signaling, and a serverless platform for easily deploying and hosting my containerized app.&lt;/p></description><content:encoded>
<![CDATA[<p>Reuse old smartphones to monitor 3D prints, with WebRTC, WebSockets and Serverless
Monitoring my 3D prints in my basement means climbing lots of stairs back and forth! So here’s my story about how I reused an old smartphone to check the status of my prints. I built a small web app that uses WebRTC to exchange video streams between my broadcasting smartphone and viewers, with WebSockets for signaling, and a serverless platform for easily deploying and hosting my containerized app.</p>
<script async class="speakerdeck-embed" data-id="3e5dfb35f64c48d9ba4a211122711b7c" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A Cloud Run service in Go calling a Workflows callback endpoint</title><link>https://glaforge.dev/posts/2022/09/27/a-cloud-run-service-in-go-calling-a-workflows-callback-endpoint/</link><pubDate>Tue, 27 Sep 2022 16:02:00 +0100</pubDate><guid>https://glaforge.dev/posts/2022/09/27/a-cloud-run-service-in-go-calling-a-workflows-callback-endpoint/</guid><description>&lt;p>It&amp;rsquo;s all &lt;a href="https://seroter.com/">Richard Seroter&lt;/a>&amp;rsquo;s fault, I ended up dabbling with &lt;a href="https://go.dev/">Golang&lt;/a>!
We were chatting about a use case using Google Cloud &lt;a href="https://cloud.google.com/workflows">Workflows&lt;/a> and a &lt;a href="https://cloud.run/">Cloud Run&lt;/a> service implemented in Go.
So it was the occasion to play a bit with Go. Well, I still don&amp;rsquo;t like error handling&amp;hellip; But let&amp;rsquo;s rewind the story a bit!&lt;/p>
&lt;p>Workflows is a fully-managed service/API orchestrator on Google Cloud. You can create some advanced business workflows using YAML syntax.
I&amp;rsquo;ve built numerous little projects using it, and &lt;a href="https://cloud.google.com/blog/topics/developers-practitioners/introducing-workflows-callbacks">blogged&lt;/a> about it.
I particularly like its ability to pause a workflow execution, creating a &lt;a href="https://cloud.google.com/workflows/docs/creating-callback-endpoints">callback endpoint&lt;/a>
that you can call from an external system to resume the execution of the workflow.
With callbacks, you&amp;rsquo;re able to implement human validation steps, for example in an expense report application
where a manager validates or rejects an expense from someone in their team (this is what I implemented in this
&lt;a href="https://cloud.google.com/blog/topics/developers-practitioners/smarter-applications-document-ai-workflows-and-cloud-functions">article&lt;/a>).&lt;/p></description><content:encoded>
<![CDATA[<p>It&rsquo;s all <a href="https://seroter.com/">Richard Seroter</a>&rsquo;s fault, I ended up dabbling with <a href="https://go.dev/">Golang</a>!
We were chatting about a use case using Google Cloud <a href="https://cloud.google.com/workflows">Workflows</a> and a <a href="https://cloud.run/">Cloud Run</a> service implemented in Go.
So it was the occasion to play a bit with Go. Well, I still don&rsquo;t like error handling&hellip; But let&rsquo;s rewind the story a bit!</p>
<p>Workflows is a fully-managed service/API orchestrator on Google Cloud. You can create some advanced business workflows using YAML syntax.
I&rsquo;ve built numerous little projects using it, and <a href="https://cloud.google.com/blog/topics/developers-practitioners/introducing-workflows-callbacks">blogged</a> about it.
I particularly like its ability to pause a workflow execution, creating a <a href="https://cloud.google.com/workflows/docs/creating-callback-endpoints">callback endpoint</a>
that you can call from an external system to resume the execution of the workflow.
With callbacks, you&rsquo;re able to implement human validation steps, for example in an expense report application
where a manager validates or rejects an expense from someone in their team (this is what I implemented in this
<a href="https://cloud.google.com/blog/topics/developers-practitioners/smarter-applications-document-ai-workflows-and-cloud-functions">article</a>).</p>
<p>For my use case with Richard, we had a workflow that was creating such a callback endpoint.
This endpoint is called from a Cloud Run service implemented in Go.
Let&rsquo;s see how to implement the workflow:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">main</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">params</span>:<span style="color:#bbb"> </span>[input]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">create_callback</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>events.create_callback_endpoint<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">http_callback_method</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;POST&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>callback_details<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">log_callback_creation</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>sys.log<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">text</span>:<span style="color:#bbb"> </span>${&#34;Callback created, awaiting calls on &#34; + callback_details.url}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">await_callback</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>events.await_callback<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">callback</span>:<span style="color:#bbb"> </span>${callback_details}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">timeout</span>:<span style="color:#bbb"> </span><span style="color:#40a070">86400</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>callback_request<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">log_callback_received</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>sys.log<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">json</span>:<span style="color:#bbb"> </span>${callback_request.http_request}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">return_callback_request</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb"> </span>${callback_request.http_request}<span style="color:#bbb">
</span></span></span></code></pre></div><p>The above workflow definition creates a callback endpoint. The URL of the callback endpoint is returned by that first step.
Then the workflow is waiting for the callback endpoint to be called externally.
The execution then resumes and logs some info about the incoming call and returns.</p>
<p>I deployed that workflow with a service account that has the Workflows Editor role, the Log Writer role (to log information),
and the Service Account Token Creator role (to create OAuth2 tokens),
as <a href="https://cloud.google.com/workflows/docs/creating-callback-endpoints#oauth-token">explained</a> in the documentation.</p>
<p>Now let&rsquo;s look at the Go service. I did a go mod init to create a new project.
I created a main.go source file with the following content:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">package</span> main
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> (
</span></span><span style="display:flex;"><span>	metadata <span style="color:#4070a0">&#34;cloud.google.com/go/compute/metadata&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#4070a0">&#34;encoding/json&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#4070a0">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#4070a0">&#34;log&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#4070a0">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#4070a0">&#34;os&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#4070a0">&#34;strings&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>The metadata module is used to fetch an OAuth2 token from the <a href="https://cloud.google.com/run/docs/container-contract#metadata-server">Cloud Run metadata server</a>.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// OAuth2 JSON struct</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">type</span> OAuth2TokenInfo <span style="color:#007020;font-weight:bold">struct</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#60a0b0;font-style:italic">// defining struct variables</span>
</span></span><span style="display:flex;"><span>	Token      <span style="color:#902000">string</span> <span style="color:#4070a0">`json:&#34;access_token&#34;`</span>
</span></span><span style="display:flex;"><span>	TokenType  <span style="color:#902000">string</span> <span style="color:#4070a0">`json:&#34;token_type&#34;`</span>
</span></span><span style="display:flex;"><span>	Expiration <span style="color:#902000">uint32</span> <span style="color:#4070a0">`json:&#34;expires_in&#34;`</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The metadata information in <code>instance/service-accounts/default/token</code> returns a JSON document that we map with the above struct.
We&rsquo;re interested in the access_token field, that we use further down to make the authenticated call to the Workflows callback endpoint.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">func</span> <span style="color:#06287e">main</span>() {
</span></span><span style="display:flex;"><span>	log.<span style="color:#06287e">Print</span>(<span style="color:#4070a0">&#34;Starting server...&#34;</span>)
</span></span><span style="display:flex;"><span>	http.<span style="color:#06287e">HandleFunc</span>(<span style="color:#4070a0">&#34;/&#34;</span>, handler)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#60a0b0;font-style:italic">// Determine port for HTTP service.	port := os.Getenv(&#34;PORT&#34;)	</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">if</span> port <span style="color:#666">==</span> <span style="color:#4070a0">&#34;&#34;</span> {		
</span></span><span style="display:flex;"><span>        port = <span style="color:#4070a0">&#34;8080&#34;</span>		
</span></span><span style="display:flex;"><span>        log.<span style="color:#06287e">Printf</span>(<span style="color:#4070a0">&#34;Defaulting to port %s&#34;</span>, port)	
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#60a0b0;font-style:italic">// Start HTTP server.	</span>
</span></span><span style="display:flex;"><span>    log.<span style="color:#06287e">Printf</span>(<span style="color:#4070a0">&#34;Listening on port %s&#34;</span>, port)	
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">if</span> err <span style="color:#666">:=</span> http.<span style="color:#06287e">ListenAndServe</span>(<span style="color:#4070a0">&#34;:&#34;</span><span style="color:#666">+</span>port, <span style="color:#007020;font-weight:bold">nil</span>); err <span style="color:#666">!=</span> <span style="color:#007020;font-weight:bold">nil</span> {		
</span></span><span style="display:flex;"><span>        log.<span style="color:#06287e">Fatal</span>(err)	
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The <code>main()</code> function starts our Go service. Let&rsquo;s now see the <code>handler()</code> function in more detail:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">func</span> <span style="color:#06287e">handler</span>(w http.ResponseWriter, r <span style="color:#666">*</span>http.Request) {
</span></span><span style="display:flex;"><span>	callbackUrl <span style="color:#666">:=</span> r.URL.<span style="color:#06287e">Query</span>().<span style="color:#06287e">Get</span>(<span style="color:#4070a0">&#34;callback_url&#34;</span>)
</span></span><span style="display:flex;"><span>	log.<span style="color:#06287e">Printf</span>(<span style="color:#4070a0">&#34;Callback URL: %s&#34;</span>, callbackUrl)
</span></span></code></pre></div><p>We retrieve the <code>?callback_url</code> query parameter that will contain our callback endpoint URL.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>	<span style="color:#60a0b0;font-style:italic">// Fetch an OAuth2 access token from the metadata server</span>
</span></span><span style="display:flex;"><span>	oauthToken, errAuth <span style="color:#666">:=</span> metadata.<span style="color:#06287e">Get</span>(<span style="color:#4070a0">&#34;instance/service-accounts/default/token&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#007020;font-weight:bold">if</span> errAuth <span style="color:#666">!=</span> <span style="color:#007020;font-weight:bold">nil</span> {
</span></span><span style="display:flex;"><span>		log.<span style="color:#06287e">Fatal</span>(errAuth)
</span></span><span style="display:flex;"><span>	}
</span></span></code></pre></div><p>Above, we make a call to the metadata server thanks to the metadata Go module.
And then we unmarshall the returned JSON document in our previously defined struct, with the following code:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>	data <span style="color:#666">:=</span> OAuth2TokenInfo{}
</span></span><span style="display:flex;"><span>	errJson <span style="color:#666">:=</span> json.<span style="color:#06287e">Unmarshal</span>([]<span style="color:#007020">byte</span>(oauthToken), <span style="color:#666">&amp;</span>data)
</span></span><span style="display:flex;"><span>	<span style="color:#007020;font-weight:bold">if</span> errJson <span style="color:#666">!=</span> <span style="color:#007020;font-weight:bold">nil</span> {
</span></span><span style="display:flex;"><span>		fmt.<span style="color:#06287e">Println</span>(errJson.<span style="color:#06287e">Error</span>())
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	log.<span style="color:#06287e">Printf</span>(<span style="color:#4070a0">&#34;OAuth2 token: %s&#34;</span>, data.Token)
</span></span></code></pre></div><p>Now it&rsquo;s time to prepare the call to our workflow callback endpoint, with a POST request:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>	workflowReq, errWorkflowReq <span style="color:#666">:=</span> http.<span style="color:#06287e">NewRequest</span>(<span style="color:#4070a0">&#34;POST&#34;</span>, callbackUrl, strings.<span style="color:#06287e">NewReader</span>(<span style="color:#4070a0">&#34;{}&#34;</span>))
</span></span><span style="display:flex;"><span>	<span style="color:#007020;font-weight:bold">if</span> errWorkflowReq <span style="color:#666">!=</span> <span style="color:#007020;font-weight:bold">nil</span> {
</span></span><span style="display:flex;"><span>		fmt.<span style="color:#06287e">Println</span>(errWorkflowReq.<span style="color:#06287e">Error</span>())
</span></span><span style="display:flex;"><span>	}
</span></span></code></pre></div><p>We add the OAuth2 token as a bearer authorization via headers:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>	workflowReq.Header.<span style="color:#06287e">Add</span>(<span style="color:#4070a0">&#34;authorization&#34;</span>, <span style="color:#4070a0">&#34;Bearer &#34;</span><span style="color:#666">+</span>data.Token)
</span></span><span style="display:flex;"><span>	workflowReq.Header.<span style="color:#06287e">Add</span>(<span style="color:#4070a0">&#34;accept&#34;</span>, <span style="color:#4070a0">&#34;application/json&#34;</span>)
</span></span><span style="display:flex;"><span>	workflowReq.Header.<span style="color:#06287e">Add</span>(<span style="color:#4070a0">&#34;content-type&#34;</span>, <span style="color:#4070a0">&#34;application/json&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    client <span style="color:#666">:=</span> <span style="color:#666">&amp;</span>http.Client{}	
</span></span><span style="display:flex;"><span>    workflowResp, workflowErr <span style="color:#666">:=</span> client.<span style="color:#06287e">Do</span>(workflowReq)	
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">if</span> workflowErr <span style="color:#666">!=</span> <span style="color:#007020;font-weight:bold">nil</span> {
</span></span><span style="display:flex;"><span>        fmt.<span style="color:#06287e">Printf</span>(<span style="color:#4070a0">&#34;Error making callback request: %s\n&#34;</span>, workflowErr)	
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    log.<span style="color:#06287e">Printf</span>(<span style="color:#4070a0">&#34;Status code: %d&#34;</span>, workflowResp.StatusCode)
</span></span><span style="display:flex;"><span>	fmt.<span style="color:#06287e">Fprintf</span>(w, <span style="color:#4070a0">&#34;Workflow callback called. Status code: %d&#34;</span>, workflowResp.StatusCode)}
</span></span></code></pre></div><p>We simply return the status code at the end of our Go service.</p>
<p>To deploy the Go service, I simply used the source deployment approach, by running gcloud run deploy,
and answering some questions (service name, region deployment, etc.)
After a couple of minutes, the service is up and running.</p>
<p>I create a new execution of the workflow from the Google Cloud console.
Once it&rsquo;s started, it logs the callback endpoint URL.
I copy its value, then I&rsquo;m calling my Cloud Run service with the <code>?callback_url=</code> query string pointing at that URL.
And voilà, the service resumes the execution of the workflow, and the workflow finishes.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Monitoring Website Changes with Workflows Cloud Functions and Sendgrid</title><link>https://glaforge.dev/posts/2022/09/12/monitoring-website-change-wwith-workflows-cloud-functions-and-sendgrid/</link><pubDate>Mon, 12 Sep 2022 15:53:44 +0100</pubDate><guid>https://glaforge.dev/posts/2022/09/12/monitoring-website-change-wwith-workflows-cloud-functions-and-sendgrid/</guid><description>&lt;p>Every year in France, around mid-September, there&amp;rsquo;s a special weekend where everyone can visit some famous places, usually closed the rest of the year. That&amp;rsquo;s &amp;ldquo;&lt;a href="https://journeesdupatrimoine.culture.gouv.fr/">Journée du Patrimoine&lt;/a>&amp;rdquo;. For example, you can visit places like the &lt;a href="https://www.elysee.fr/">Elysée Palace&lt;/a> or the &lt;a href="https://www.gouvernement.fr/le-patrimoine-de-l-hotel-de-matignon">Matignon Palace&lt;/a>, if you want to see where the French president, or the French prime minister work. However, for some of those places, it&amp;rsquo;s tricky to register online to book a slot, as there&amp;rsquo;s always a high demand for them. Furthermore, you have to be there at the right time to register, and often, you don&amp;rsquo;t even know when that day or time is! So I thought I could monitor the website of the Elysée Palace to see when the registration would open, by tracking changes on the Elysée website.&lt;/p></description><content:encoded>
<![CDATA[<p>Every year in France, around mid-September, there&rsquo;s a special weekend where everyone can visit some famous places, usually closed the rest of the year. That&rsquo;s &ldquo;<a href="https://journeesdupatrimoine.culture.gouv.fr/">Journée du Patrimoine</a>&rdquo;. For example, you can visit places like the <a href="https://www.elysee.fr/">Elysée Palace</a> or the <a href="https://www.gouvernement.fr/le-patrimoine-de-l-hotel-de-matignon">Matignon Palace</a>, if you want to see where the French president, or the French prime minister work. However, for some of those places, it&rsquo;s tricky to register online to book a slot, as there&rsquo;s always a high demand for them. Furthermore, you have to be there at the right time to register, and often, you don&rsquo;t even know when that day or time is! So I thought I could monitor the website of the Elysée Palace to see when the registration would open, by tracking changes on the Elysée website.</p>
<p>To monitor web page or website changes, there are a ton of online services available. There are often some limitations to the number of free requests, or to the frequency of the change checks. Being a developer on Google Cloud, I decided to write a simple solution that would take advantage of various Google Cloud services, namely:</p>
<ul>
<li><a href="https://cloud.google.com/workflows">Workflows</a>: to define the various steps of my site change workflow,</li>
<li><a href="https://cloud.google.com/scheduler">Cloud Scheduler</a>: to call execute my workflow on a regular basis,</li>
<li><a href="https://cloud.google.com/functions">Cloud Functions</a>: to compute a hash of the webpage, to see if the page changed,</li>
<li><a href="https://cloud.google.com/storage">Cloud Storage</a>: to store the hashes,</li>
<li><a href="https://sendgrid.com/">SendGrid</a> (not a Google Cloud product): to send me an email when changes have appeared,</li>
<li><a href="https://cloud.google.com/secret-manager">Secret Manager</a>: to store my SendGrid API key securely.</li>
</ul>
<p>Let&rsquo;s have a look first at a function that computes the hash of a webpage. As there&rsquo;s no hash function in the Workflows standard library, I decided to use a function to do that job. I used the Node.js runtime, with the crypto module, which contains a sha1 implementation:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> crypto <span style="color:#666">=</span> require(<span style="color:#4070a0">&#39;crypto&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>exports.checksum <span style="color:#666">=</span> (req, res) =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">const</span> webpageBody <span style="color:#666">=</span> req.body.webpage;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">const</span> shasum <span style="color:#666">=</span> crypto.createHash(<span style="color:#4070a0">&#39;sha1&#39;</span>);
</span></span><span style="display:flex;"><span>  shasum.update(webpageBody);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">const</span> sha1 <span style="color:#666">=</span> shasum.digest(<span style="color:#4070a0">&#39;hex&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  res.status(<span style="color:#40a070">200</span>).send({sha1<span style="color:#666">:</span> sha1});
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>The function receives the web page content from the workflow. Then I create the sha1 hash with that content, and return it in hexadecimal form, in a small JSON payload.</p>
<p>I created a Google Cloud Storage bucket to contain my web page hashes:</p>
<p><figure>
  <a href="#img-ae15e196b7e42832b8226d82cf901d5d">
    <img src="/img/patrimoine/patrimoine-gcs.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-ae15e196b7e42832b8226d82cf901d5d">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/patrimoine/patrimoine-gcs.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Since I&rsquo;m using SendGrid to notify me by email on changes, I store the API key securely in Secret Manager:</p>
<p><figure>
  <a href="#img-6eccdf4e1e358dc00106162f3b41b26d">
    <img src="/img/patrimoine/patrimoine-secret.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-6eccdf4e1e358dc00106162f3b41b26d">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/patrimoine/patrimoine-secret.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Now let&rsquo;s zoom on our workflow, piece by piece.<br />
First, I define some variables, like the name of my bucket, the name of my hashes text file, and I retrieve my SendGrid API key (see this previous <a href="https://glaforge.dev/posts/2022/02/04/using-the-secret-manager-connector-for-workflows-to-call-an-authenticated-service/">article about using Secret Manager with Workflows</a>):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">main</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">params</span>:<span style="color:#bbb"> </span>[input]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">assignment</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>- <span style="color:#062873;font-weight:bold">bucket</span>:<span style="color:#bbb"> </span>hash_results<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>- <span style="color:#062873;font-weight:bold">file_name</span>:<span style="color:#bbb"> </span>hash.txt<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">get_email_api_key</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>googleapis.secretmanager.v1.projects.secrets.versions.accessString<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">secret_id</span>:<span style="color:#bbb"> </span>SENDGRID_API_KEY<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>EMAIL_API_KEY<span style="color:#bbb">
</span></span></span></code></pre></div><p>Then I read the content of the previous hash in GCS (you can also check this article on how to <a href="https://glaforge.dev/posts/2022/01/21/reading-in-and-writing-a-json-file-to-a-storage-bucket-from-a-workflow/">read and write JSON data to a file in a bucket from a workflow</a>):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">read_hash_from_gcs</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>http.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb"> </span>${&#34;https://storage.googleapis.com/download/storage/v1/b/&#34; + bucket + &#34;/o/&#34; + file_name}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">auth</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span>OAuth2<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">query</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">alt</span>:<span style="color:#bbb"> </span>media<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>hash_from_gcs<span style="color:#bbb">
</span></span></span></code></pre></div><p>It&rsquo;s time to make a simple HTTP GET call to the website. Currently, the URL is hard-coded, but we could parameterize the workflow to get that URL from the workflow execution input parameters instead.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">retrieve_web_page</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>http.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">           </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb"> </span>https://evenements.elysee.fr/<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>web_page<span style="color:#bbb">
</span></span></span></code></pre></div><p>Once I retrieved the content of the URL (the result of that request is stored in the web_page variable), I can compute my hash, by calling my cloud function:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">compute_hash</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>http.post<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb"> </span>https://europe-west1-patrimoine-check.cloudfunctions.net/checksum<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">body</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">webpage</span>:<span style="color:#bbb"> </span>${web_page.body}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>hash_result<span style="color:#bbb">
</span></span></span></code></pre></div><p>That&rsquo;s where we introduce some branching in the workflow. If the web page hasn&rsquo;t changed we finish early, but if it has changed, then we&rsquo;re going to store the new hash in GCS:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">assign_hashes</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>- <span style="color:#062873;font-weight:bold">old_hash</span>:<span style="color:#bbb"> </span>${hash_from_gcs.body.sha1}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>- <span style="color:#062873;font-weight:bold">new_hash</span>:<span style="color:#bbb"> </span>${hash_result.body.sha1}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>- <span style="color:#062873;font-weight:bold">hash_msg</span>:<span style="color:#bbb"> </span>${&#34;Old hash = &#34; + old_hash + &#34; / New hash = &#34; + new_hash}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">conditionalSwitch</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">switch</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">condition</span>:<span style="color:#bbb"> </span>${new_hash != old_hash}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">next</span>:<span style="color:#bbb"> </span>write_hash_to_gcs<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">next</span>:<span style="color:#bbb"> </span>returnOutput<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">write_hash_to_gcs</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>http.post<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb"> </span>${&#34;https://storage.googleapis.com/upload/storage/v1/b/&#34; + bucket + &#34;/o&#34;}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">auth</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span>OAuth2<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">query</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">name</span>:<span style="color:#bbb"> </span>${file_name}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">body</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">sha1</span>:<span style="color:#bbb"> </span>${hash_result.body.sha1}<span style="color:#bbb">
</span></span></span></code></pre></div><p>I log the fact the website has changed, and I&rsquo;m calling the SendGrid API (like in this article on using SendGrid for sending emails from Workflows):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">site_changed_log</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>sys.log<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">text</span>:<span style="color:#bbb"> </span>Website has changed<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">notify_by_email</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>http.post<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb"> </span>https://api.sendgrid.com/v3/mail/send<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">headers</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">Content-Type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;application/json&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">Authorization</span>:<span style="color:#bbb"> </span>${&#34;Bearer &#34; + EMAIL_API_KEY}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">body</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">personalizations</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>- <span style="color:#062873;font-weight:bold">to</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span>- <span style="color:#062873;font-weight:bold">email</span>:<span style="color:#bbb"> </span>me@gmail.com<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">from</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span><span style="color:#062873;font-weight:bold">email</span>:<span style="color:#bbb"> </span>you@gmail.com<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">subject</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Elysée, page mise à jour&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">content</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>- <span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span>text/plain<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                      </span><span style="color:#062873;font-weight:bold">value</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;La page de l&#39;Élysée a été mise à jour&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">log_hashes</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>sys.log<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">text</span>:<span style="color:#bbb"> </span>${hash_msg}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">returnOutput</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb"> </span>${hash_msg}<span style="color:#bbb">
</span></span></span></code></pre></div><p>The workflows need to be invoked at a regular interval of time. Workflows can be configured to be invoked on a schedule via Cloud Scheduler (again, check this article on scheduling workflow executions). I configured my workflow to be triggered every minute, with the * * * * * cron pattern.<br />
<figure>
  <a href="#img-dee38a3adf490f0d1fbca33d9a146a7f">
    <img src="/img/patrimoine/patrimoine-schedule.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-dee38a3adf490f0d1fbca33d9a146a7f">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/patrimoine/patrimoine-schedule.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>And voila! I have my little workflow being invoked every minute to check if a web page has changed, and send me an email if so!</p>
<p>To be honest with you, the workflow worked perfectly&hellip; but the true story is that I wasn&rsquo;t monitoring the right URL, I should have monitored the front page instead. Furthermore, the page I was monitoring included some dynamic JavaScript code, but the HTML fetched wasn&rsquo;t really changing. I missed the registration window, and all the slots filled super rapidly before I even had the time to register my family for a visit! Shame on me, better check my URL next time, or create webpage screenshots with a headless Chrome running in Cloud Run or in Cloud Functions! Or, of course, use online services that have solved those problems with their years of experience! Hopefully, next year, I won&rsquo;t miss the registration! But it was fun to glue together all those useful services from Google Cloud, to solve a concrete problem.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Some beans and gems, some snakes and elephants, with Java 17, Ruby 3, Python 3.10, and PHP 8.1 in App Engine and Cloud Functions</title><link>https://glaforge.dev/posts/2022/04/14/some-beans-and-gems-some-snakes-and-elephants-with-java-17-ruby-3-python-310-and-php-81-in-app-engine-and-cloud-functions/</link><pubDate>Thu, 14 Apr 2022 14:03:46 +0100</pubDate><guid>https://glaforge.dev/posts/2022/04/14/some-beans-and-gems-some-snakes-and-elephants-with-java-17-ruby-3-python-310-and-php-81-in-app-engine-and-cloud-functions/</guid><description>&lt;p>Time to spill the beans and show the gems, to our friendly snakes and elephants: we&amp;rsquo;ve got some great news for Java, Ruby, Python and PHP serverless developers today. Google App Engine and Cloud Functions are adding new modern runtimes, allowing you to update to the major version release trains of those programming languages.&lt;/p>
&lt;p>In short, here&amp;rsquo;s what&amp;rsquo;s new:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Access to App Engine legacy bundled services for Java 11/17, Python 3 and Go 1.12+ runtimes, is &lt;strong>Generally Available&lt;/strong>&lt;/p></description><content:encoded>
<![CDATA[<p>Time to spill the beans and show the gems, to our friendly snakes and elephants: we&rsquo;ve got some great news for Java, Ruby, Python and PHP serverless developers today. Google App Engine and Cloud Functions are adding new modern runtimes, allowing you to update to the major version release trains of those programming languages.</p>
<p>In short, here&rsquo;s what&rsquo;s new:</p>
<ul>
<li>
<p>Access to App Engine legacy bundled services for Java 11/17, Python 3 and Go 1.12+ runtimes, is <strong>Generally Available</strong></p>
</li>
<li>
<p>The Java 17, Ruby 3.0, Python 3.10, and PHP 8.1 runtimes come into preview in App Engine and Cloud Functions</p>
</li>
</ul>
<p>Let&rsquo;s have a closer look. First of all, the access to App Engine legacy bundled services for second generation runtimes for <a href="https://cloud.google.com/appengine/docs/standard/java-gen2/services/access">Java</a>, <a href="https://cloud.google.com/appengine/docs/standard/python3/services/access">Python</a> and <a href="https://cloud.google.com/appengine/docs/standard/go/services/access">Go</a> is now Generally Available. In the past, for example for the Java platform, only Java 8 (a first generation runtime) could access the <a href="https://cloud.google.com/appengine/docs/standard/java-gen2/reference/services/bundled">built-in APIs</a> like Memcache, Images, Mail, or Task Queues. Now, if you use the Java 11 runtime (a second generation runtime), you can access those services as well as all the <a href="https://cloud.google.com/java/docs/reference">Google Cloud APIs</a>. For example, you can now store transient cached data in Memcache, or send an email to users of your application in a second generation runtime. Same thing for Python and Go developers, you can take advantage of the bundled services as well. If you&rsquo;re still using an old runtime version, this will further ease the transition to newer versions. Be sure to check it out and upgrade.</p>
<p>Next, let&rsquo;s continue with a fresh bean and a shiny gem, mixed in with some friendly animals, with the <strong>preview of the Java 17, Ruby 3.0, Python 3.10 and PHP 8.1 runtimes for both App Engine and Cloud Functions</strong>. What about having a look at what&rsquo;s new in those language versions?</p>
<h2 id="java">Java</h2>
<p>Between the two Long-Term-Support versions of Java 11 and 17, a lot of new features have landed. Java developers can now write text blocks for strings spanning several lines, without having to concatenate multiple strings manually. The switch construct has evolved to become an expression, which lets you break away from the <code>break</code> keyword, and paves the way for more advanced pattern matching capabilities. Speaking of which, the <code>instanceof</code> keyword is indeed offering some pattern matching evolution, to avoid obvious but useless casts. Records allow you to create more streamlined immutable data classes, rather than writing your own Java beans for that purpose with proper <code>hashCode()</code>, <code>equals()</code> or <code>toString()</code> methods. For more control over your class hierarchy, sealed class gives you more control over the extension of your classes.</p>
<h2 id="ruby">Ruby</h2>
<p>With Ruby 3.0, the big highlights were on performance, static type checking, and concurrency. The goal to make Ruby 3.0, three times faster on some benchmarks than Ruby 2.0 was reached, making your code run more swiftly. Also, Ruby programs can be annotated with some typing information, which allow type checkers to take advantage of those types to provide static type checking, to improve the quality of your code. For concurrency and parallelism, a new actor-inspired concurrency primitive called Ractor helps taming multiple cores in parallel, for your demanding workloads. And a fiber scheduler is introduced for intercepting blocking operations. And beyond those headlines, many improvements to various Ruby APIs have also landed.</p>
<h2 id="python">Python</h2>
<p>In Python 3.10, the parser gives better and clearer error messages for syntax errors (with more accurate error location), also for indentation, attribute, and name errors, which greatly help developers to find the problems in their code. Structural pattern matching lands with a new <code>match</code> and <code>case</code> statement construct. Further PEP improvements are tackling more robust type hints for static type checkers. Parenthesized context managers have been added to make the code prettier when spanning a long collection of context managers across several lines. </p>
<h2 id="php">PHP</h2>
<p>With version 8.1, PHP gets a pretty major update. First, let&rsquo;s start with a new <code>enum</code> syntax construct instead of creating constants in classes, and you get validation out of the box. Classes now have the ability to define final class constants. The new <code>readonly</code> properties can&rsquo;t be changed after initialization, which is great for value objects and DTOs. A first class callable syntax is introduced, allowing you to get a reference to any function, with a short syntax. Developers will also find further improvements to initializers, that make it possible to even have nested attributes, using objects as default parameter values, static variables, and global constants. One last nice addition we can mention is the introduction of fibers to implement lightweight cooperative concurrency.</p>
<h2 id="your-turn">Your turn</h2>
<p>Gems, beans, elephants, snakes: there&rsquo;s something great in those new language versions for every developer. Thus, with these new runtimes in Preview, Java, Ruby, Python and PHP developers can update or develop new App Engine apps and Cloud Functions using the latest and greatest versions of their favorite languages. Be sure to check out the documentation for App Engine (<a href="https://cloud.google.com/appengine/docs/standard/java-gen2/runtime">Java</a>, <a href="https://cloud.google.com/appengine/docs/standard/ruby/runtime">Ruby</a>, <a href="https://cloud.google.com/appengine/docs/standard/python3/runtime">Python</a>, <a href="https://cloud.google.com/appengine/docs/standard/php7/runtime">PHP</a>) and Cloud Functions (<a href="https://cloud.google.com/functions/docs/concepts/java-runtime">Java</a>, <a href="https://cloud.google.com/functions/docs/concepts/ruby-runtime">Ruby</a>, <a href="https://cloud.google.com/functions/docs/concepts/python-runtime">Python</a>, <a href="https://cloud.google.com/functions/docs/concepts/php-runtime">PHP</a>). We&rsquo;re looking forward to hearing from you about how you&rsquo;ll take advantage of those new language runtimes.</p>
<p><a href="https://cloud.google.com/blog/products/serverless/introducing-the-next-generation-of-cloud-functions"></a></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Schedule a Workflow Execution</title><link>https://glaforge.dev/posts/2022/02/09/schedule-a-workflow-execution/</link><pubDate>Wed, 09 Feb 2022 17:06:51 +0100</pubDate><guid>https://glaforge.dev/posts/2022/02/09/schedule-a-workflow-execution/</guid><description>&lt;p>There are &lt;a href="https://cloud.google.com/workflows/docs/executing-workflow">different ways to launch the execution&lt;/a> of a workflow. In previous articles, we mentioned that you can &lt;a href="https://cloud.google.com/workflows/docs/quickstart-gcloud">use the gcloud command-line&lt;/a> tool to create an execution, you can also use the various &lt;a href="https://cloud.google.com/workflows/docs/quickstart-client-libraries">client libraries&lt;/a> to invoke Workflows, or use the &lt;a href="https://cloud.google.com/workflows/docs/reference/executions/rest">REST API&lt;/a>. A workflow itself can also invoke other workflows!&lt;/p>
&lt;p>But today, I&amp;rsquo;d like to tell you how to schedule the execution of a workflow. For that purpose, we&amp;rsquo;ll take advantage of &lt;a href="https://cloud.google.com/scheduler">Cloud Scheduler&lt;/a>. The &lt;a href="https://cloud.google.com/workflows/docs/schedule-workflow">documentation&lt;/a> is actually covering this topic in detail, so be sure to grab all the info there. However, I&amp;rsquo;ll go quickly through the steps, and tell you about a nice new feature in the cloud console to ease the scheduling of workflows!&lt;/p></description><content:encoded>
<![CDATA[<p>There are <a href="https://cloud.google.com/workflows/docs/executing-workflow">different ways to launch the execution</a> of a workflow. In previous articles, we mentioned that you can <a href="https://cloud.google.com/workflows/docs/quickstart-gcloud">use the gcloud command-line</a> tool to create an execution, you can also use the various <a href="https://cloud.google.com/workflows/docs/quickstart-client-libraries">client libraries</a> to invoke Workflows, or use the <a href="https://cloud.google.com/workflows/docs/reference/executions/rest">REST API</a>. A workflow itself can also invoke other workflows!</p>
<p>But today, I&rsquo;d like to tell you how to schedule the execution of a workflow. For that purpose, we&rsquo;ll take advantage of <a href="https://cloud.google.com/scheduler">Cloud Scheduler</a>. The <a href="https://cloud.google.com/workflows/docs/schedule-workflow">documentation</a> is actually covering this topic in detail, so be sure to grab all the info there. However, I&rsquo;ll go quickly through the steps, and tell you about a nice new feature in the cloud console to ease the scheduling of workflows!</p>
<p>First, you need to have both Workflows and Cloud Scheduler enabled:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud services <span style="color:#007020">enable</span> cloudscheduler.googleapis.com workflows.googleapis.com
</span></span></code></pre></div><p>Cloud Scheduler will need a service account with <code>workflows.invoker</code> role, to be allowed to call Workflows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud iam service-accounts create workflows_caller_sa
</span></span><span style="display:flex;"><span>gcloud projects add-iam-policy-binding MY_PROJECT_ID <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>  --member serviceAccount:workflows_caller_sa@MY_PROJECT_ID.iam.gserviceaccount.com <span style="color:#4070a0;font-weight:bold">\\</span>
</span></span><span style="display:flex;"><span>  --role roles/workflows.invoker
</span></span></code></pre></div><p>Now it&rsquo;s time to create the cron job:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud scheduler <span style="color:#007020">jobs</span> create http every_5_minute_schedule <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --schedule<span style="color:#666">=</span><span style="color:#4070a0">&#34;*/5 * * * *&#34;</span> <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --uri<span style="color:#666">=</span><span style="color:#4070a0">&#34;https://workflowexecutions.googleapis.com/v1/projects/MY_PROJECT_ID/locations/REGION_NAME/workflows/WORKFLOW_NAME/executions&#34;</span> <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --message-body<span style="color:#666">=</span><span style="color:#4070a0">&#34;{\&#34;argument\&#34;: \&#34;DOUBLE_ESCAPED_JSON_STRING\&#34;}&#34;</span> <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --time-zone<span style="color:#666">=</span><span style="color:#4070a0">&#34;America/New_York&#34;</span> <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --oauth-service-account-email<span style="color:#666">=</span><span style="color:#4070a0">&#34;workflows_caller_sa@MY_PROJECT_ID.iam.gserviceaccount.com&#34;</span>
</span></span></code></pre></div><p>Here, you can see that Scheduler will run every 5 minutes (using the cron notation), and that it&rsquo;s going to call the Workflows REST API to create a new execution. You can also pass an argument for the workflow input.</p>
<p>The cool new feature I was eager to mention today was the direct integration of the scheduling as part of the Workflows creation flow, in the cloud console.</p>
<p>Now, when you create a new workflow, you can select a trigger:</p>
<p><figure>
  <a href="#img-e5f619bee96e60094321deaaef391a22">
    <img src="/img/schedule-workflow/scheduler-trigger-1-600.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-e5f619bee96e60094321deaaef391a22">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/schedule-workflow/scheduler-trigger-1-600.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Click on the <code>ADD NEW TRIGGER</code> button, and select <code>Scheduler</code>. A side panel on the right will show up, and you will be able to specify the schedule to create, directly integrated, instead of having to head over to the Cloud Scheduler product section:</p>
<p><figure>
  <a href="#img-68eebade558b59676036f9a4ca947ad0">
    <img src="/img/schedule-workflow/scheduler-trigger-2-600.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-68eebade558b59676036f9a4ca947ad0">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/schedule-workflow/scheduler-trigger-2-600.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>And there, you can specify the various details of the schedule! It&rsquo;s nice to see both products nicely integrated, to ease the flow of creating a scheduled workflow.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Using the Secret Manager connector for Workflows to call an authenticated service</title><link>https://glaforge.dev/posts/2022/02/04/using-the-secret-manager-connector-for-workflows-to-call-an-authenticated-service/</link><pubDate>Fri, 04 Feb 2022 22:26:59 +0100</pubDate><guid>https://glaforge.dev/posts/2022/02/04/using-the-secret-manager-connector-for-workflows-to-call-an-authenticated-service/</guid><description>&lt;p>Workflows allows you to call APIs, whether from or hosted on Google Cloud, or any external API in the wild.
A few days ago, for example, we saw an example on
&lt;a href="https://glaforge.dev/posts/2022/02/01/sending-an-email-with-sendgrid-from-workflows/">how to use the SendGrid API to send emails from a workflow&lt;/a>.
However, in that article, I had the API key hard-coded into my workflow, which is a bad practice.
Instead, we can store secrets in &lt;a href="https://cloud.google.com/secret-manager">Secret Manager&lt;/a>.
Workflows has a specific &lt;a href="https://cloud.google.com/workflows/docs/reference/googleapis/secretmanager/Overview">connector for Secret Manager&lt;/a>,
and a useful method to access secrets.&lt;/p></description><content:encoded>
<![CDATA[<p>Workflows allows you to call APIs, whether from or hosted on Google Cloud, or any external API in the wild.
A few days ago, for example, we saw an example on
<a href="https://glaforge.dev/posts/2022/02/01/sending-an-email-with-sendgrid-from-workflows/">how to use the SendGrid API to send emails from a workflow</a>.
However, in that article, I had the API key hard-coded into my workflow, which is a bad practice.
Instead, we can store secrets in <a href="https://cloud.google.com/secret-manager">Secret Manager</a>.
Workflows has a specific <a href="https://cloud.google.com/workflows/docs/reference/googleapis/secretmanager/Overview">connector for Secret Manager</a>,
and a useful method to access secrets.</p>
<p>In this article, we&rsquo;ll learn two things:</p>
<ul>
<li>How to access secrets stored in Secret Manager with the Workflows connector</li>
<li>How to call an API that requires basic authentication</li>
</ul>
<p>Let&rsquo;s access the secrets I need to do my basic auth call to the API I need to call:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">get_secret_user</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>googleapis.secretmanager.v1.projects.secrets.versions.accessString<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">secret_id</span>:<span style="color:#bbb">  </span>basicAuthUser<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb">  </span>secret_user<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">get_secret_password</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>googleapis.secretmanager.v1.projects.secrets.versions.accessString<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">secret_id</span>:<span style="color:#bbb">  </span>basicAuthPassword<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb">  </span>secret_password<span style="color:#bbb">
</span></span></span></code></pre></div><p>The user login and password are now stored in variables that I can reuse in my workflow.
I will create the Base64 encoded user:password string required to pass in the authorization header:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">assign_user_password</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">encodedUserPassword</span>:<span style="color:#bbb">  </span>${base64.encode(text.encode(secret_user  +  &#34;:&#34;  +  secret_password))}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Equipped with my encoded user:password string, I can now call my API (here a cloud function)
by added an authorization header with basic authentication (and return the output of the function):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">call_function</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>http.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb">  </span>https://europe-west1-workflows-days.cloudfunctions.net/basicAuthFn<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">headers</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">Authorization</span>:<span style="color:#bbb">  </span>${&#34;Basic  &#34; + encodedUserPassword}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>fn_output<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">return_result</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb"> </span>${fn_output.body}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Workflows has built-in
<a href="https://cloud.google.com/workflows/docs/authentication#making_authenticated_requests">OAuth2 and OIDC support for authenticating to Google hosted APIs, functions and Cloud Run services</a>,
but it&rsquo;s also useful to know how to invoke other authenticated services, like those requiring basic auth, or other bearer tokens.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Upload and use JSON data in your workflow from GCS</title><link>https://glaforge.dev/posts/2022/02/03/upload-and-use-json-data-in-your-workflow-from-gcs/</link><pubDate>Thu, 03 Feb 2022 22:39:13 +0100</pubDate><guid>https://glaforge.dev/posts/2022/02/03/upload-and-use-json-data-in-your-workflow-from-gcs/</guid><description>&lt;p>Following up the &lt;a href="https://glaforge.dev/posts/2022/01/21/reading-in-and-writing-a-json-file-to-a-storage-bucket-from-a-workflow/">article&lt;/a>
on writing and reading JSON files in cloud storage buckets (GCS), we saw that we could access the data of the JSON file, and use it in our workflow.
Let&amp;rsquo;s have a look at a concrete use of this.&lt;/p>
&lt;p>Today, we&amp;rsquo;ll take advantage of this mechanism to avoid hard-coding the URLs of the APIs we call from our workflow.
That way, it makes the workflow more portable across environments.&lt;/p></description><content:encoded>
<![CDATA[<p>Following up the <a href="https://glaforge.dev/posts/2022/01/21/reading-in-and-writing-a-json-file-to-a-storage-bucket-from-a-workflow/">article</a>
on writing and reading JSON files in cloud storage buckets (GCS), we saw that we could access the data of the JSON file, and use it in our workflow.
Let&rsquo;s have a look at a concrete use of this.</p>
<p>Today, we&rsquo;ll take advantage of this mechanism to avoid hard-coding the URLs of the APIs we call from our workflow.
That way, it makes the workflow more portable across environments.</p>
<p>Let&rsquo;s regroup the logic for reading and loading the JSON data in a reusable subworkflow:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">read_env_from_gcs</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">params</span>:<span style="color:#bbb">  </span>[bucket,  object]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">read_from_gcs</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>http.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb">  </span>${&#34;https://storage.googleapis.com/download/storage/v1/b/&#34;  +  bucket  +  &#34;/o/&#34;  +  object}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">auth</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb">  </span>OAuth2<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">query</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">alt</span>:<span style="color:#bbb">  </span>media<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb">  </span>env_file_json_content<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">return_content</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span>${env_file_json_content.body}<span style="color:#bbb">
</span></span></span></code></pre></div><p>You call this subworkflow with two parameters: the bucket name, and the object or file name that you want to load.</p>
<p>Now let&rsquo;s use it from the main workflow. We need a first step to call the subworkflow to load a specific file from a specific bucket.
The subworkflow below will return the content of the JSON data in the env_details variable.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">​​main</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">params</span>:<span style="color:#bbb">  </span>[input]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">load_env_details</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>read_env_from_gcs<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">bucket</span>:<span style="color:#bbb">  </span>workflow_environment_info<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">object</span>:<span style="color:#bbb">  </span>env-info.json<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb">  </span>env_details<span style="color:#bbb">
</span></span></span></code></pre></div><p>Imagine the JSON file contains a JSON object with a <code>SERVICE_URL</code> key, pointing at the URL of a service,
then you can call the service with the following expression: <code>${env_details.SERVICE_URL}</code> as shown below.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">call_service</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>http.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb">  </span>${env_details.SERVICE_URL}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb">  </span>service_result<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">return_result</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span>${service_result.body}<span style="color:#bbb">
</span></span></span></code></pre></div><p>This is great for avoiding hardcoding certain values in your workflow definitions.
However, for true environment-specific deployments, this is not yet ideal, as you would have to point to a different file in the bucket, or use a different bucket.
And that information is currently hardcoded in the definition when you make the call to the subworkflow.
But if you follow some naming conventions for the project names and bucket names, that map to environments, this can work!
(ie. <code>PROD_bucket</code> vs <code>DEV_bucket</code>, or <code>PROD-env-info.json</code> vs <code>DEV-env-info.json</code>)</p>
<p>Let&rsquo;s wait for the support of environment variables in Workflows!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Sending an email with SendGrid from Workflows</title><link>https://glaforge.dev/posts/2022/02/01/sending-an-email-with-sendgrid-from-workflows/</link><pubDate>Tue, 01 Feb 2022 21:04:31 +0100</pubDate><guid>https://glaforge.dev/posts/2022/02/01/sending-an-email-with-sendgrid-from-workflows/</guid><description>&lt;p>Following up the &lt;a href="https://glaforge.dev/posts/2022/01/21/reading-in-and-writing-a-json-file-to-a-storage-bucket-from-a-workflow/">article&lt;/a>
on writing and reading JSON files in cloud storage buckets, we saw that we could access the data of the JSON file,
and use it in our workflow. Let&amp;rsquo;s have a look at a concrete use of this.&lt;/p>
&lt;p>Today, we&amp;rsquo;ll take advantage of this mechanism to avoid hard-coding the URLs of the APIs we call from our workflow.
That way, it makes the workflow more portable across environments.&lt;/p></description><content:encoded>
<![CDATA[<p>Following up the <a href="https://glaforge.dev/posts/2022/01/21/reading-in-and-writing-a-json-file-to-a-storage-bucket-from-a-workflow/">article</a>
on writing and reading JSON files in cloud storage buckets, we saw that we could access the data of the JSON file,
and use it in our workflow. Let&rsquo;s have a look at a concrete use of this.</p>
<p>Today, we&rsquo;ll take advantage of this mechanism to avoid hard-coding the URLs of the APIs we call from our workflow.
That way, it makes the workflow more portable across environments.</p>
<p>Let&rsquo;s regroup the logic for reading and loading the JSON data in a reusable subworkflow:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">read_env_from_gcs</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">params</span>:<span style="color:#bbb">  </span>[bucket,  object]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">read_from_gcs</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>http.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb">  </span>${&#34;https://storage.googleapis.com/download/storage/v1/b/&#34;  +  bucket  +  &#34;/o/&#34;  +  object}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">auth</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb">  </span>OAuth2<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">query</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">alt</span>:<span style="color:#bbb">  </span>media<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb">  </span>env_file_json_content<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">return_content</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span>${env_file_json_content.body}<span style="color:#bbb">
</span></span></span></code></pre></div><p>You call this subworkflow with two parameters: the bucket name, and the object or file name that you want to load.</p>
<p>Now let&rsquo;s use it from the main workflow. We need a first step to call the subworkflow to load a specific file from a specific bucket.
The subworkflow below will return the content of the JSON data in the <code>env_details</code> variable.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">​​main</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">params</span>:<span style="color:#bbb">  </span>[input]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">load_env_details</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>read_env_from_gcs<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">bucket</span>:<span style="color:#bbb">  </span>workflow_environment_info<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">object</span>:<span style="color:#bbb">  </span>env-info.json<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb">  </span>env_details<span style="color:#bbb">
</span></span></span></code></pre></div><p>Imagine the JSON file contains a JSON object with a <code>SERVICE_URL</code> key, pointing at the URL of a service,
then you can call the service with the following expression: <code>${env_details.SERVICE_URL}</code> as shown below.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">call_service</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>http.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb">  </span>${env_details.SERVICE_URL}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb">  </span>service_result<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">return_result</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span>${service_result.body}<span style="color:#bbb">
</span></span></span></code></pre></div><p>This is great for avoiding hardcoding certain values in your workflow definitions.
However, for true environment-specific deployments, this is not yet ideal,
as you would have to point to a different file in the bucket, or use a different bucket.
And that information is currently hardcoded in the definition when you make the call to the subworkflow.
But if you follow some naming conventions for the project names and bucket names, that map to environments,
this can work! (ie. <code>PROD_bucket</code> vs <code>DEV_bucket</code>, or <code>PROD-env-info.json</code> vs <code>DEV-env-info.json</code>)</p>
<p>Let&rsquo;s wait for the support of environment variables in Workflows!
For notification purposes, especially in an asynchronous way, email is a great solution.
I wanted to add an email notification step in <a href="https://cloud.google.com/workflows">Google Cloud Workflows</a>.
Since GCP doesn&rsquo;t have an email service, I looked at the various email services available in the cloud: SendGrid, Mailgun, Mailjet,
and even ran a quick Twitter <a href="https://twitter.com/glaforge/status/1488444661211533312">poll</a> to see what folks in the wild are using.
I experimented with SendGrid, and the <a href="https://signup.sendgrid.com/">sign up</a> process was pretty straightforward,
as I was able to <a href="https://docs.sendgrid.com/for-developers/sending-email/api-getting-started">get started</a> quickly,
by creating an API key, and sending my first email with cURL command.</p>
<p>Now comes the part where I needed to call that API from my workflow definition.
And that&rsquo;s actually pretty straightforward as well.
Let&rsquo;s see that in action:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">retrieve_api_key</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">SENDGRID_API_KEY</span>:<span style="color:#bbb">  </span><span style="color:#4070a0">&#34;MY_API_KEY&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">send_email</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>http.post<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb">  </span>https://api.sendgrid.com/v3/mail/send<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">headers</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">Content-Type</span>:<span style="color:#bbb">  </span><span style="color:#4070a0">&#34;application/json&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">Authorization</span>:<span style="color:#bbb">  </span>${&#34;Bearer  &#34; + SENDGRID_API_KEY}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">body</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">personalizations</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>- <span style="color:#062873;font-weight:bold">to</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>- <span style="color:#062873;font-weight:bold">email</span>:<span style="color:#bbb"> </span>to@example.com<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">from</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">email</span>:<span style="color:#bbb"> </span>from@example.com<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">subject</span>:<span style="color:#bbb"> </span>Sending an email from Workflows<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">content</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>- <span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span>text/plain<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                  </span><span style="color:#062873;font-weight:bold">value</span>:<span style="color:#bbb"> </span>Here&#39;s the body of my email<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>email_result<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">return_result</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb"> </span>${email_result.body}<span style="color:#bbb">
</span></span></span></code></pre></div><p>In the <code>retrieve_api_key</code> step, I simply hard-coded the SendGrid API key.
However, you can of course store that secret in Secret Manager,
and then fetch the secret key thanks to the Workflows Secret Manager connector (that&rsquo;s probably worth a dedicated article!)</p>
<p>Then, in the <code>send_email</code> step, I prepare my HTTP POST request to the SendGrid API endpoint.
I specify the content type, and of course, the authorization using the SendGrid API key.
Next, I prepare the body of that request, describing my email,
with a <code>from</code> field with a registered email user that I defined in SendGrid, a <code>to</code> field corresponding to the recipient,
an email <code>subject</code> and <code>body</code> (just plain text, here).
And that&rsquo;s pretty much it!
I just translated the JSON body sent in the <a href="https://app.sendgrid.com/guide/integrate/langs/curl">cURL example</a> from SendGrid&rsquo;s documentation,
into YAML (using a handy JSON to YAML conversion <a href="https://www.json2yaml.com/">utility</a>)</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Smarter Applications With Document Ai Workflows and Cloud Functions</title><link>https://glaforge.dev/posts/2022/02/01/smarter-applications-with-document-ai-workflows-and-cloud-functions/</link><pubDate>Tue, 01 Feb 2022 13:35:51 +0100</pubDate><guid>https://glaforge.dev/posts/2022/02/01/smarter-applications-with-document-ai-workflows-and-cloud-functions/</guid><description>&lt;p>At enterprises across industries, documents are at the center of core business processes. Documents store a treasure trove of valuable information whether it&amp;rsquo;s a company&amp;rsquo;s invoices, HR documents, tax forms and much more. However, the unstructured nature of documents make them difficult to work with as a data source. We call this &lt;a href="https://www.gartner.com/en/information-technology/glossary/dark-data">&amp;ldquo;dark data&amp;rdquo;&lt;/a> or unstructured data that businesses collect, process and store but do not utilize for purposes such as analytics, monetization, etc. These documents in pdf or image formats, often trigger complex processes that have historically relied on fragmented technology and manual steps. With compute solutions on Google Cloud and &lt;a href="https://cloud.google.com/document-ai">Document AI&lt;/a>, you can create seamless integrations and easy to use applications for your users. Document AI is a platform and a family of solutions that help businesses to transform documents into structured data backed by machine learning. In this blog post we&amp;rsquo;ll walk you through how to use Serverless technology to process documents with &lt;a href="https://cloud.google.com/functions">Cloud Functions&lt;/a>, and with workflows of business processes orchestrating microservices, API calls, and functions, thanks to &lt;a href="https://cloud.google.com/workflows">Workflows&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>At enterprises across industries, documents are at the center of core business processes. Documents store a treasure trove of valuable information whether it&rsquo;s a company&rsquo;s invoices, HR documents, tax forms and much more. However, the unstructured nature of documents make them difficult to work with as a data source. We call this <a href="https://www.gartner.com/en/information-technology/glossary/dark-data">&ldquo;dark data&rdquo;</a> or unstructured data that businesses collect, process and store but do not utilize for purposes such as analytics, monetization, etc. These documents in pdf or image formats, often trigger complex processes that have historically relied on fragmented technology and manual steps. With compute solutions on Google Cloud and <a href="https://cloud.google.com/document-ai">Document AI</a>, you can create seamless integrations and easy to use applications for your users. Document AI is a platform and a family of solutions that help businesses to transform documents into structured data backed by machine learning. In this blog post we&rsquo;ll walk you through how to use Serverless technology to process documents with <a href="https://cloud.google.com/functions">Cloud Functions</a>, and with workflows of business processes orchestrating microservices, API calls, and functions, thanks to <a href="https://cloud.google.com/workflows">Workflows</a>.</p>
<p>At Cloud Next 2021, we presented how to build easy <a href="https://cloud.withgoogle.com/next/catalog?session=DEV202#application-development">AI-powered applications with Google Cloud</a>. We introduced a sample application for handling incoming expense reports, analyzing expense receipts with <a href="https://cloud.google.com/solutions/procurement-doc-ai">Procurement Document A</a>I, a DocAI solution for automating procurement data capture from forms including invoices, utility statements and more. Then organizing the logic of a report approval process with Workflows, and used Cloud Functions as glue to invoke the workflow, and do analysis of the parsed document.</p>
<p><figure>
  <a href="#img-392efee994103a4297281beee0a4de87">
    <img src="/img/smart-expense/smart-expenses-screens.max-1700x1700.png"
      alt="/img/smart-expense/smart-expenses-screens.max-1700x1700.png"
       />
  </a>
  <figcaption>/img/smart-expense/smart-expenses-screens.max-1700x1700.png</figcaption>
</figure>
<div class="lightbox" id="img-392efee994103a4297281beee0a4de87">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/smart-expense/smart-expenses-screens.max-1700x1700.png"
    alt="/img/smart-expense/smart-expenses-screens.max-1700x1700.png"
     />
  <div class="lightbox-caption">/img/smart-expense/smart-expenses-screens.max-1700x1700.png</div>
</div>
</p>
<p>We also open sourced the code on this <a href="https://github.com/GoogleCloudPlatform/smart-expenses">Github repository</a>, if you&rsquo;re interested in learning more about this application.</p>
<p><figure>
  <a href="#img-82b426d725666ed075313751834cc68b">
    <img src="/img/smart-expense/architecture-diagram.max-1500x1500.png"
      alt="/img/smart-expense/architecture-diagram.max-1500x1500.png"
       />
  </a>
  <figcaption>/img/smart-expense/architecture-diagram.max-1500x1500.png</figcaption>
</figure>
<div class="lightbox" id="img-82b426d725666ed075313751834cc68b">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/smart-expense/architecture-diagram.max-1500x1500.png"
    alt="/img/smart-expense/architecture-diagram.max-1500x1500.png"
     />
  <div class="lightbox-caption">/img/smart-expense/architecture-diagram.max-1500x1500.png</div>
</div>
</p>
<p>In the above diagram, there are two user journeys: the employee submitting an expense report where multiple receipts are processed at once, and the manager validating or rejecting the expense report. </p>
<p>First, the employee goes to the website, powered by <a href="https://vuejs.org/">Vue.js</a> for the frontend progressive JavaScript framework and <a href="https://shoelace.style/">Shoelace</a> for the library of web components. The website is hosted via <a href="https://firebase.google.com/docs/hosting">Firebase Hosting</a>. The frontend invokes an <a href="https://cloud.google.com/functions/docs/writing/http">HTTP function</a> that triggers the execution of our business <a href="https://github.com/GoogleCloudPlatform/smart-expenses/blob/main/workflow.yaml">workflow</a>, defined using the Workflows YAML syntax. </p>
<p>Workflows is able to handle long-running operations without any additional code required, in our case we are asynchronously processing a set receipt files. Here, the Document AI <a href="https://cloud.google.com/blog/topics/developers-practitioners/better-service-orchestration-workflows">connector</a> directly calls the batch processing endpoint for service. This API returns a long-running operation: if you poll the API, the operation state will be &ldquo;RUNNING&rdquo; until it has reached a &ldquo;SUCCEEDED&rdquo; or &ldquo;FAILED&rdquo; state. You would have to wait for its completion. However, Workflows&rsquo; <a href="https://cloud.google.com/workflows/docs/connectors">connectors</a> handle such long-running operations, without you having to poll the API multiple times till the state changes. Here&rsquo;s how we call the batch processing operation of the <a href="https://cloud.google.com/workflows/docs/reference/googleapis/documentai/Overview">Document AI connector</a>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">invoke_document_ai</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>googleapis.documentai.v1.projects.locations.processors.batchProcess<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">name</span>:<span style="color:#bbb"> </span>${&#34;projects/&#34; + project + &#34;/locations/eu/processors/&#34; + processorId}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">location</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;eu&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">body</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">inputDocuments</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">gcsPrefix</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">gcsUriPrefix</span>:<span style="color:#bbb"> </span>${bucket_input + report_id}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">documentOutputConfig</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">gcsOutputConfig</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">gcsUri</span>:<span style="color:#bbb"> </span>${bucket_output + report_id}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">skipHumanReview</span>:<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">true</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>document_ai_response<span style="color:#bbb">
</span></span></span></code></pre></div><p>Machine learning uses state of the art Vision and Natural Language Processing models to intelligently extract schematized data from documents with Document AI. As a developer, you don&rsquo;t have to figure out how to fine tune or reframe the receipt pictures, or how to find the relevant field and information in the receipt. It&rsquo;s Document AI&rsquo;s job to help you here: it will return a JSON document whose fields are: <code>line_item</code>, <code>currency</code>, <code>supplier_name</code>, <code>total_amount</code>, etc. Document AI is capable of understanding standardized papers and forms, including invoices, lending documents, pay slips, driver licenses, and more.</p>
<p>A cloud function retrieves all the relevant fields of the receipts, and makes its own tallies, before submitting the expense report for approval to the manager. Another useful feature of Workflows is put to good use: Callbacks, that we <a href="https://cloud.google.com/blog/topics/developers-practitioners/introducing-workflows-callbacks">introduced</a> last year. In the workflow definition we create a callback endpoint, and the workflow execution will wait for the callback to be called to continue its flow, thanks to those two instructions:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">create_callback</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>events.create_callback_endpoint<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">http_callback_method</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;POST&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>callback_details<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#0e84b5;font-weight:bold">...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">await_callback</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">try</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>events.await_callback<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">callback</span>:<span style="color:#bbb"> </span>${callback_details}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">timeout</span>:<span style="color:#bbb"> </span><span style="color:#40a070">3600</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>callback_request<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">except</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">as</span>:<span style="color:#bbb"> </span>e<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>- <span style="color:#062873;font-weight:bold">update_status_to_error</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#0e84b5;font-weight:bold">...</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>In this example application, we combined the intelligent capabilities of Document AI to transform complex image documents into usable structured data, with Cloud Functions for data transformation, process triggering, and callback handling logic, and Workflows enabled us to orchestrate the underlying business process and its service call logic.</p>
<h2 id="going-further">Going further </h2>
<p>If you&rsquo;re looking to make sense of your documents, turning dark data into structured information, be sure to check out what <a href="https://cloud.google.com/document-ai">Document AI</a> offers. You can also get your hands on a <a href="https://codelabs.developers.google.com/codelabs/docai-form-parser-v3-node#0">codelab</a> to get started quickly, in which you&rsquo;ll get a chance at processing handwritten forms. If you want to explore <a href="https://cloud.google.com/workflows">Workflows</a>, <a href="https://cloud.google.com/workflows/docs/quickstarts">quickstarts</a> are available to guide you through your first steps, and likewise, another <a href="https://codelabs.developers.google.com/codelabs/cloud-workflows-intro?hl=en#0">codelab</a> explores the basics of Workflows. As mentioned earlier, for a concrete example, the source code of our <a href="https://github.com/GoogleCloudPlatform/smart-expenses">smart expense application</a> is available on Github. Don&rsquo;t hesitate to reach out to us at <a href="https://twitter.com/glaforge">@glaforge</a> and <a href="https://twitter.com/asrivas_dev">@asrivas_dev</a> to discuss smart scalable apps with us.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Open sourcing the App Engine Standard Java Runtime</title><link>https://glaforge.dev/posts/2022/01/26/open-sourcing-the-app-engine-standard-java-runtime/</link><pubDate>Wed, 26 Jan 2022 19:07:57 +0100</pubDate><guid>https://glaforge.dev/posts/2022/01/26/open-sourcing-the-app-engine-standard-java-runtime/</guid><description>&lt;p>One year after Google App Engine was &lt;a href="http://googleappengine.blogspot.com/2008/04/introducing-google-app-engine-our-new.html">released&lt;/a> in 2008, Java became the &lt;a href="http://googleappengine.blogspot.com/2009/04/seriously-this-time-new-language-on-app.html">second language runtime&lt;/a> available on the platform. Java developers were able to deploy and scale their servlet-based web applications easily, without worrying about infrastructure management. Not only Java was able to run then, but alternative JVM languages, like &lt;a href="https://spring.io/blog/2009/04/08/write-your-google-app-engine-applications-in-groovy">Apache Groovy&lt;/a>, and &lt;a href="https://kotlinlang.org/">Kotlin&lt;/a> are also part of the game. Fast forward to today, we&amp;rsquo;re pleased to announce that the Java Runtime for App Engine is now available as open source, in the &lt;a href="https://github.com/GoogleCloudPlatform/appengine-java-standard">GoogleCloudPlatform/appengine-java-standard&lt;/a> repository on Github.&lt;/p></description><content:encoded>
<![CDATA[<p>One year after Google App Engine was <a href="http://googleappengine.blogspot.com/2008/04/introducing-google-app-engine-our-new.html">released</a> in 2008, Java became the <a href="http://googleappengine.blogspot.com/2009/04/seriously-this-time-new-language-on-app.html">second language runtime</a> available on the platform. Java developers were able to deploy and scale their servlet-based web applications easily, without worrying about infrastructure management. Not only Java was able to run then, but alternative JVM languages, like <a href="https://spring.io/blog/2009/04/08/write-your-google-app-engine-applications-in-groovy">Apache Groovy</a>, and <a href="https://kotlinlang.org/">Kotlin</a> are also part of the game. Fast forward to today, we&rsquo;re pleased to announce that the Java Runtime for App Engine is now available as open source, in the <a href="https://github.com/GoogleCloudPlatform/appengine-java-standard">GoogleCloudPlatform/appengine-java-standard</a> repository on Github.</p>
<h2 id="whats-available">What&rsquo;s available?</h2>
<p>The <code>appengine-java-standard</code> repository contains the Java source code for the Java standard environment, the production runtime, the APIs, and the local SDK. </p>
<p>Zooming on the various directories of the project, you&rsquo;ll find the <a href="https://github.com/GoogleCloudPlatform/appengine-java-standard/tree/main/api/src/main/java/com/google/appengine/api">APIs</a> from the <code>com.google.appengine.api</code> package for using and accessing App Engine services like Datastore to store your data, Blobstore to save your binary blobs, Taskqueue to enqueue computing tasks, Memcache to cache expensive results you don&rsquo;t want to recompute, or Urlfetch to call external services. </p>
<p>The great developer experience of Google App Engine comes in part from the fact you can run your application locally on your development machine, thanks to the local development environment. The aforementioned services all have a local <a href="https://github.com/GoogleCloudPlatform/appengine-java-standard/tree/main/api_dev/src/main/java/com/google/appengine/api">implementation</a> enabling you to start your app on your machine, rather than having to deploy in the cloud each time you make a change.</p>
<p>To use the services in the cloud, however, you can use Google&rsquo;s built-in <a href="https://cloud.google.com/appengine/docs/standard/java/tools/remoteapi">remote APIs</a> (<a href="https://github.com/GoogleCloudPlatform/appengine-java-standard/tree/main/remoteapi/src/main/java/com/google/appengine/tools/remoteapi">code</a>) that let you transparently access App Engine services from any Java application. For example, you can use the Remote API to access a production Datastore from an app running on your local machine. You can also use Remote API to access the Datastore of one App Engine application from a different App Engine application.</p>
<p>In the <a href="https://github.com/GoogleCloudPlatform/appengine-java-standard/tree/main/runtime/impl/src/main/java/com/google/apphosting">runtime folder</a>, you&rsquo;ll be able to see how your App Engine app is started, with the underlying <a href="https://www.eclipse.org/jetty/">Jetty</a> servlet container, and understand how the various components or services are configured on startup.</p>
<p>What&rsquo;s not open source today are the specific layers that tie App Engine to the underlying <a href="https://research.google.com/pubs/pub43438.html?hl=es">Borg</a> cluster management system, internal to the Google infrastructure, as developers can&rsquo;t replicate the whole Google backend easily in their own environment.</p>
<h2 id="why-make-it-open-source">Why make it open source?</h2>
<p>It&rsquo;s important for our customers to not be locked in a particular tool, environment, or vendor, and give them the liberty to run their workloads elsewhere, and understand how we actually run their code. That&rsquo;s why Google Cloud follows an <a href="https://cloud.google.com/open-cloud">Open Cloud</a> approach, and participates actively in the open source ecosystem. Our open cloud approach enables you to develop software faster, innovate more easily, and scale more efficiently&mdash;while also reducing technology risk. Through the years, we&rsquo;ve open sourced various technologies, like <a href="https://kubernetes.io/">Kubernetes</a>, based on our experience of running containers at scale, or the <a href="https://gvisor.dev/">gVisor</a> sandbox, an application kernel for containers that provides efficient defense-in-depth anywhere, and which was pioneered by the App Engine environment, as gVisor was its sandbox environment.</p>
<p>By open sourcing the App Engine Java runtime, we&rsquo;re making the first step towards letting you run the whole App Engine environment wherever you want: on your local development environment, on-premise in your own data center, or potentially on our other computing platforms like <a href="https://cloud.google.com/run/">Cloud Run</a>. We&rsquo;re also making a key step towards easier transitions for future runtimes based on newer Long-Term Support versions of the Java programming language. </p>
<p>Finally, being able to compile, test and release the Java runtime environment for Java 8, Java 11 and soon Java 17 from an open source platform is much easier than relying on the huge internal Google mono repository system which is intended to support only one single version of everything, including a single JDK toolchain. We are releasing the binary artifacts in the <a href="https://repo1.maven.org/maven2/com/google/appengine/">Maven central repository</a>, including Javadocs and sources to find the relevant code regarding exceptions raised during runtime execution.</p>
<h2 id="going-further">Going further</h2>
<p>If you&rsquo;re not familiar with App Engine yet, be sure to check out our <a href="https://cloud.google.com/appengine">online resources</a> about this powerful and scalable serverless application platform on our website. And get started with the tutorials on the <a href="https://cloud.google.com/appengine/docs/standard/java/runtime">Java 8</a> or <a href="https://cloud.google.com/appengine/docs/standard/java11/services/access">Java 11 standard environment</a>. To learn more about this open source App Engine Java runtime, please visit our <a href="https://github.com/GoogleCloudPlatform/appengine-java-standard">repository</a> on Github, to discover how the serverless secret sauce is made. And if you&rsquo;re feeling like helping and <a href="https://github.com/GoogleCloudPlatform/appengine-java-standard/blob/main/CONTRIBUTING.md">contributing</a>, we&rsquo;re looking forward to hearing from you, by filing new <a href="https://github.com/GoogleCloudPlatform/appengine-java-standard/issues">tickets</a> or by preparing some <a href="https://github.com/GoogleCloudPlatform/appengine-java-standard/pulls">pull requests</a>.</p>
<p><a href="https://cloud.google.com/blog/products/application-development/turn-it-up-to-eleven-java-11-runtime-comes-to-app-engine"></a></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Reading in and writing a JSON file to a storage bucket from a workflow</title><link>https://glaforge.dev/posts/2022/01/21/reading-in-and-writing-a-json-file-to-a-storage-bucket-from-a-workflow/</link><pubDate>Fri, 21 Jan 2022 21:28:16 +0100</pubDate><guid>https://glaforge.dev/posts/2022/01/21/reading-in-and-writing-a-json-file-to-a-storage-bucket-from-a-workflow/</guid><description>&lt;p>&lt;a href="https://cloud.google.com/workflows">Workflows&lt;/a> provides several &lt;a href="https://cloud.google.com/workflows/docs/reference/googleapis">connectors&lt;/a>
for interacting with various Google Cloud APIs and services.
In the past, I&amp;rsquo;ve used for example the &lt;a href="https://cloud.google.com/workflows/docs/reference/googleapis/documentai/Overview">Document AI connector&lt;/a>
to parse documents like expense receipts,
or the &lt;a href="https://cloud.google.com/workflows/docs/reference/googleapis/secretmanager/Overview">Secret Manager connector&lt;/a>
to store and access secrets like passwords.
Another useful connector I was interested in using today was the
&lt;a href="https://cloud.google.com/workflows/docs/reference/googleapis/storage/Overview">Google Cloud Storage connector&lt;/a>,
to store and read files stored in storage buckets.&lt;/p>
&lt;p>Those connectors are auto-generated from their API discovery descriptors,
but there are some limitations currently that prevent, for example, to download the content of a file.
So instead of using the connector, I looked at the JSON API for cloud storage to see what it offered
(&lt;a href="https://cloud.google.com/workflows/docs/reference/googleapis/storage/v1/objects/insert">insert&lt;/a> and
&lt;a href="https://cloud.google.com/storage/docs/json_api/v1/objects/get">get&lt;/a> methods).&lt;/p></description><content:encoded>
<![CDATA[<p><a href="https://cloud.google.com/workflows">Workflows</a> provides several <a href="https://cloud.google.com/workflows/docs/reference/googleapis">connectors</a>
for interacting with various Google Cloud APIs and services.
In the past, I&rsquo;ve used for example the <a href="https://cloud.google.com/workflows/docs/reference/googleapis/documentai/Overview">Document AI connector</a>
to parse documents like expense receipts,
or the <a href="https://cloud.google.com/workflows/docs/reference/googleapis/secretmanager/Overview">Secret Manager connector</a>
to store and access secrets like passwords.
Another useful connector I was interested in using today was the
<a href="https://cloud.google.com/workflows/docs/reference/googleapis/storage/Overview">Google Cloud Storage connector</a>,
to store and read files stored in storage buckets.</p>
<p>Those connectors are auto-generated from their API discovery descriptors,
but there are some limitations currently that prevent, for example, to download the content of a file.
So instead of using the connector, I looked at the JSON API for cloud storage to see what it offered
(<a href="https://cloud.google.com/workflows/docs/reference/googleapis/storage/v1/objects/insert">insert</a> and
<a href="https://cloud.google.com/storage/docs/json_api/v1/objects/get">get</a> methods).</p>
<p>What I wanted to do was to store a JSON document, and to read a JSON document.
I haven&rsquo;t tried with other media types yet, like pictures or other binary files.
Anyhow, here&rsquo;s how to write a JSON file into a cloud storage bucket:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">main</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">params</span>:<span style="color:#bbb">  </span>[input]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">assignment</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>- <span style="color:#062873;font-weight:bold">bucket</span>:<span style="color:#bbb">  </span>YOUR_BUCKET_NAME_HERE<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">write_to_gcs</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>http.post<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb">  </span>${&#34;https://storage.googleapis.com/upload/storage/v1/b/&#34;  +  bucket  +  &#34;/o&#34;}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">auth</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb">  </span>OAuth2<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">query</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">name</span>:<span style="color:#bbb">  </span>THE_FILE_NAME_HERE<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">body</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">name</span>:<span style="color:#bbb">  </span>Guillaume<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">age</span>:<span style="color:#bbb">  </span><span style="color:#40a070">99</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>In the file, I&rsquo;m storing a JSON document that contains a couple keys, defined in the body of that call.
By default, here, a JSON media type is assumed, so the body defined at the bottom in YAML is actually written as JSON in the resulting file.
Oh and of course, don&rsquo;t forget to change the names of the bucket and the object in the example above.</p>
<p>And now, here&rsquo;s how you can read the content of the file from the bucket:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">main</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">params</span>:<span style="color:#bbb">  </span>[input]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">assignment</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>- <span style="color:#062873;font-weight:bold">bucket</span>:<span style="color:#bbb">  </span>YOUR_BUCKET_NAME_HERE<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>- <span style="color:#062873;font-weight:bold">name</span>:<span style="color:#bbb">  </span>THE_FILE_NAME_HERE<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">read_from_gcs</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>http.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb">  </span>${&#34;https://storage.googleapis.com/download/storage/v1/b/&#34;  +  bucket  +  &#34;/o/&#34;  +  name}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">auth</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb">  </span>OAuth2<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">query</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">alt</span>:<span style="color:#bbb">  </span>media<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb">  </span>data_json_content<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">return_content</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span>${data_json_content.body}<span style="color:#bbb">
</span></span></span></code></pre></div><p>This time we change the GCS URL from <code>upload</code> to <code>download</code>,
and we use the <code>alt=media</code> query parameter to instruct the GCS JSON API that we want to retrieve the content of the file (not just its metadata).
In the end, we return the body of that call, which contains the content.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>How to get the project ID in a Java Cloud Function</title><link>https://glaforge.dev/posts/2022/01/17/how-to-get-the-project-id-in-a-java-cloud-function/</link><pubDate>Mon, 17 Jan 2022 22:20:29 +0100</pubDate><guid>https://glaforge.dev/posts/2022/01/17/how-to-get-the-project-id-in-a-java-cloud-function/</guid><description>&lt;p>As I was working with my colleague &lt;a href="https://cloud.google.com/developers/advocates/sara-ford">Sara Ford&lt;/a>
on testing the Cloud Functions runtimes for the upcoming &amp;ldquo;second generation&amp;rdquo; of the product,
rebased on the &lt;a href="https://cloud.run/">Cloud Run&lt;/a> platform, I wrote a few simple functions for the Java runtime.
In one of those Java functions, I wanted to use Google Cloud Storage, to download a file from a bucket.
I took a look at the existing
&lt;a href="https://github.com/googleapis/google-cloud-java/blob/main/google-cloud-examples/src/main/java/com/google/cloud/examples/storage/objects/DownloadObject.java">sample&lt;/a>
to download an object:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>Storage&lt;span style="color:#bbb"> &lt;/span>storage&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#bbb"> &lt;/span>StorageOptions.&lt;span style="color:#4070a0">newBuilder&lt;/span>()&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">    &lt;/span>.&lt;span style="color:#4070a0">setProjectId&lt;/span>(projectId)&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">    &lt;/span>.&lt;span style="color:#4070a0">build&lt;/span>()&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">    &lt;/span>.&lt;span style="color:#4070a0">getService&lt;/span>();&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>Blob&lt;span style="color:#bbb"> &lt;/span>blob&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#bbb"> &lt;/span>storage.&lt;span style="color:#4070a0">get&lt;/span>(BlobId.&lt;span style="color:#4070a0">of&lt;/span>(bucketName,&lt;span style="color:#bbb"> &lt;/span>objectName));&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>blob.&lt;span style="color:#4070a0">downloadTo&lt;/span>(Paths.&lt;span style="color:#4070a0">get&lt;/span>(destFilePath));&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I know the name of the bucket, the name of the file, I&amp;rsquo;m going to store the file in the local file system.
So I have all the information needed&amp;hellip; except the project ID within which I deployed my Java cloud function.
So how do I get the project ID, in Java, inside the Cloud Functions environment?&lt;/p></description><content:encoded>
<![CDATA[<p>As I was working with my colleague <a href="https://cloud.google.com/developers/advocates/sara-ford">Sara Ford</a>
on testing the Cloud Functions runtimes for the upcoming &ldquo;second generation&rdquo; of the product,
rebased on the <a href="https://cloud.run/">Cloud Run</a> platform, I wrote a few simple functions for the Java runtime.
In one of those Java functions, I wanted to use Google Cloud Storage, to download a file from a bucket.
I took a look at the existing
<a href="https://github.com/googleapis/google-cloud-java/blob/main/google-cloud-examples/src/main/java/com/google/cloud/examples/storage/objects/DownloadObject.java">sample</a>
to download an object:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Storage<span style="color:#bbb"> </span>storage<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>StorageOptions.<span style="color:#4070a0">newBuilder</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">setProjectId</span>(projectId)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">build</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">getService</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Blob<span style="color:#bbb"> </span>blob<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>storage.<span style="color:#4070a0">get</span>(BlobId.<span style="color:#4070a0">of</span>(bucketName,<span style="color:#bbb"> </span>objectName));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>blob.<span style="color:#4070a0">downloadTo</span>(Paths.<span style="color:#4070a0">get</span>(destFilePath));<span style="color:#bbb">
</span></span></span></code></pre></div><p>I know the name of the bucket, the name of the file, I&rsquo;m going to store the file in the local file system.
So I have all the information needed&hellip; except the project ID within which I deployed my Java cloud function.
So how do I get the project ID, in Java, inside the Cloud Functions environment?</p>
<p>A previous iteration of Cloud Functions had various useful environment variables available, which included the project ID.
So you could retrieve the ID with a <code>System.getenv()</code> call.
However, for various compatibility reasons between the various runtimes,
with the <a href="https://knative.dev/docs/">Knative</a> open source project, that variable disappeared along the road.</p>
<p>However, I know that the project ID is also part of the internal
<a href="https://cloud.google.com/appengine/docs/standard/java/accessing-instance-metadata">compute metadata</a>
that is accessible via a special URL:</p>
<p><a href="http://metadata.google.internal/computeMetadata/v1/project/project-id">http://metadata.google.internal/computeMetadata/v1/project/project-id</a></p>
<p>With that knowledge in mind, I thought I could simply make a quick HTTP request to get that information:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">getProjectId</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>String<span style="color:#bbb"> </span>projectId<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>HttpURLConnection<span style="color:#bbb"> </span>conn<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">null</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>URL<span style="color:#bbb"> </span>url<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>URL(<span style="color:#4070a0">&#34;http://metadata.google.internal/computeMetadata/v1/project/project-id&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>conn<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>(HttpURLConnection)(url.<span style="color:#4070a0">openConnection</span>());<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>conn.<span style="color:#4070a0">setRequestProperty</span>(<span style="color:#4070a0">&#34;Metadata-Flavor&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Google&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>projectId<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>String(conn.<span style="color:#4070a0">getInputStream</span>().<span style="color:#4070a0">readAllBytes</span>(),<span style="color:#bbb"> </span>StandardCharsets.<span style="color:#4070a0">UTF_8</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>conn.<span style="color:#4070a0">disconnect</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">catch</span><span style="color:#bbb"> </span>(Throwable<span style="color:#bbb"> </span>t)<span style="color:#bbb"> </span>{}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>projectId;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>For the call to work, it is mandatory to set the <code>Metadata-Flavor</code> header that you see above.
I used Java&rsquo;s built-in <code>HttpURLConnection</code> for the job.
There are other HTTP libraries that could&rsquo;ve made the code simpler, but at first,
I didn&rsquo;t want to bring another HTTP client, just for retrieving a simple project meta-information.</p>
<p>I&rsquo;m one of the developers who designed the
<a href="https://github.com/GoogleCloudPlatform/functions-framework-java">Functions Framework for Java</a>
that is used to craft cloud functions in Java, however, I&rsquo;ve written quite a few functions using Node.js as well.
And in the Node ecosystem, there&rsquo;s actually an NPM module whose responsibility is to retrieve such project metadata.
With the <a href="https://www.npmjs.com/package/gcp-metadata">gcp-metadata</a> module, you can require it and then fetch the project ID with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> gcpMetadata <span style="color:#666">=</span> require(<span style="color:#4070a0">&#39;gcp-metadata&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> projectId <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">await</span> gcpMetadata.project(<span style="color:#4070a0">&#39;project-id&#39;</span>);
</span></span></code></pre></div><p>I was surprised I couldn&rsquo;t easily find an equivalent library in Java.
It took me a while to find it, but it actually exists too!
That&rsquo;s the <a href="https://googleapis.dev/java/google-cloud-core/latest/index.html">com.google.cloud:google-cloud-core</a> library!
And it&rsquo;s trivial to use:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.cloud.ServiceOptions</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>String<span style="color:#bbb"> </span>projectId<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>ServiceOptions.<span style="color:#4070a0">getDefaultProjectId</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>An extra dependency in my pom.xml, one import and one static method call on <code>ServiceOptions</code>,
and I can get the GCP project ID! So I&rsquo;m now able to pass the project ID to my <code>StorageOptions</code> builder.
But for some reason, I recalled that at times, in some other projects I had written,
I remembered not really needing that project ID information, as the libraries
I was using were smart enough to infer such information from the environment.
Let&rsquo;s look again at the <code>StorageOptions</code> from the beginning.
What if I simply omit the <code>setProjectId()</code> method call?
Lo and behold&hellip; indeed, it was actually not required, and the project ID was inferred, transparently.
So I didn&rsquo;t really need to search for how to retrieve this project ID at all!
And actually, you can further simplify the creation of the StorageOptions down to:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Storage<span style="color:#bbb"> </span>storage<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>StorageOptions<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">getDefaultInstance</span>()<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>.<span style="color:#4070a0">getService</span>();<span style="color:#bbb">
</span></span></span></code></pre></div><p>At least, now, I know how to retrieve the project ID in Java,
in case the libraries or the environment are not providing such details on their own!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Introducing Workflows callbacks</title><link>https://glaforge.dev/posts/2021/10/02/introducing-workflows-callbacks/</link><pubDate>Sun, 03 Oct 2021 10:38:29 +0100</pubDate><guid>https://glaforge.dev/posts/2021/10/02/introducing-workflows-callbacks/</guid><description>&lt;p>With &lt;a href="http://cloud.google.com/workflows">Workflows&lt;/a>, developers can easily orchestrate various services together, on Google Cloud or third-party APIs. Workflows &lt;a href="https://cloud.google.com/blog/topics/developers-practitioners/introducing-new-connectors-workflows">connectors&lt;/a> handle long-running operations of Google Cloud services till completion. And Workflow executions can also wait for time to pass with the built-in &lt;a href="https://cloud.google.com/workflows/docs/reference/stdlib/sys/sleep">&lt;code>sys.sleep function&lt;/code>&lt;/a>, till some computation finishes, or some event takes place. &lt;/p>
&lt;p>But what if you need some user input or some approval in the middle of the workflow execution, like validating automatic text translation? Or an external system like a fulfillment center or an inventory system that is going to notify that products are back in stock? Instead of using a combination of &amp;ldquo;sleep&amp;rdquo; instructions and API polling, now you&amp;rsquo;ll be able to use Workflows callbacks! &lt;/p></description><content:encoded>
<![CDATA[<p>With <a href="http://cloud.google.com/workflows">Workflows</a>, developers can easily orchestrate various services together, on Google Cloud or third-party APIs. Workflows <a href="https://cloud.google.com/blog/topics/developers-practitioners/introducing-new-connectors-workflows">connectors</a> handle long-running operations of Google Cloud services till completion. And Workflow executions can also wait for time to pass with the built-in <a href="https://cloud.google.com/workflows/docs/reference/stdlib/sys/sleep"><code>sys.sleep function</code></a>, till some computation finishes, or some event takes place. </p>
<p>But what if you need some user input or some approval in the middle of the workflow execution, like validating automatic text translation? Or an external system like a fulfillment center or an inventory system that is going to notify that products are back in stock? Instead of using a combination of &ldquo;sleep&rdquo; instructions and API polling, now you&rsquo;ll be able to use Workflows callbacks! </p>
<p>With callbacks, the execution of a workflow can wait until it receives a call to a specific callback endpoint. Let&rsquo;s have a look at a concrete example.</p>
<h2 id="case-study-human-validation-of-automated-translation">Case study: human validation of automated translation</h2>
<p>Let&rsquo;s have a look at a concrete example! Machine learning based translations have reached an incredible level of quality, but sometimes, you want a human being to validate the translations produced. Thanks to Workflows callbacks, we can add a human, or an autonomous system, into the loop.</p>
<p>To illustrate this case study, the following diagram will show you a possible implementation of the whole process:</p>
<p><figure>
  <a href="#img-0837dee3eebe07a63b60ef60e115929e">
    <img src="/img/workflows-days/architecture-translation.max-1200x1200.png"
      alt="/img/workflows-days/architecture-translation.max-1200x1200.png"
       />
  </a>
  <figcaption>/img/workflows-days/architecture-translation.max-1200x1200.png</figcaption>
</figure>
<div class="lightbox" id="img-0837dee3eebe07a63b60ef60e115929e">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-days/architecture-translation.max-1200x1200.png"
    alt="/img/workflows-days/architecture-translation.max-1200x1200.png"
     />
  <div class="lightbox-caption">/img/workflows-days/architecture-translation.max-1200x1200.png</div>
</div>
</p>
<ol>
<li>First, the user visits a translation web page. They fill a textarea with the text they want to translate, and click on the translate button.</li>
<li>Clicking on the button will call a Cloud Function that will launch an execution of the workflow. The text to translate is passed as a parameter of the function, and as a parameter of the workflow too.</li>
<li>The text is saved in Cloud Firestore, and the Translation API is called with the input text, and will return the translation, which will be stored in Firestore as well. The translation appears on the web page in real-time thanks to the Firebase SDK.</li>
<li>A step in the workflow creates a callback endpoint (also saved in Firestore), so that it can be called to validate or reject the automatic translation. When the callback endpoint is saved in Firestore, the web page displays validation and rejection buttons.</li>
<li>The workflow now explicitly awaits the callback endpoint to be called. This pauses the workflow execution.</li>
<li>The user decides to either validate or reject the translation. When one of the two buttons is clicked, a Cloud Function is called, with the approval status as parameter, which will in turn call the callback endpoint created by the workflow, also passing the approval status. The workflow resumes its execution, and saves the approval in Firestore. And this is the end of our workflow.</li>
</ol>
<h2 id="creating-a-callback-and-awaiting-incoming-calls">Creating a callback and awaiting incoming calls</h2>
<p>Two new built-in functions are introduced in the standard Workflows library:</p>
<ul>
<li>
<p><a href="https://cloud.google.com/workflows/docs/reference/stdlib/events/create_callback_endpoint"><code>events.create_callback_endpoint</code></a> &mdash; to create and setup the callback endpoint</p>
</li>
<li>
<p><a href="https://cloud.google.com/workflows/docs/reference/stdlib/events/await_callback"><code>events.await_callback</code></a> &mdash; to wait for the callback endpoint to be called</p>
</li>
</ul>
<p>With <code>events.create_callback_endpoint</code> you specify the HTTP method that should be used for invoking the callback endpoint, and you get a dictionary with the URL of that endpoint that you can pass to other systems. And with <code>events.await_callback</code>, you pass the callback endpoint to wait on, pass a timeout defining how long you want to wait, and when the endpoint is called, you get access to the body that was sent to the endpoint.</p>
<p>Let&rsquo;s have a look at the <a href="https://github.com/GoogleCloudPlatform/workflows-demos/blob/master/callback-translation/translation-validation.yaml#L73">YAML definition</a> of our workflow, where we apply those two new functions. First, we&rsquo;re going to create the callback:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">create_callback</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>events.create_callback_endpoint<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">http_callback_method</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;POST&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>callback_details<span style="color:#bbb">
</span></span></span></code></pre></div><p>The callback endpoint is now ready to receive incoming requests via a <code>POST HTTP</code> method, and the details of that endpoint are stored in the <code>callback_details</code> dictionary (in particular, the url key will be associated with the URL of the endpoint).</p>
<p>Next, we pause the workflow, and await the callback with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">await_callback</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>events.await_callback<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">callback</span>:<span style="color:#bbb"> </span>${callback_details}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">timeout</span>:<span style="color:#bbb"> </span><span style="color:#40a070">3600</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>callback_request<span style="color:#bbb">
</span></span></span></code></pre></div><p>The <code>callback_details</code> from earlier is passed as argument, as well as a timeout in seconds to wait for the callback to be made. When the call is received, all the details of the request are stored in the <code>callback_request</code> dictionary. You then have access to the full HTTP request, including its headers or its body. In case the timeout is reached, a <code>TimeoutError</code> is raised and can be caught by a <code>try /</code> <code>except</code> block.</p>
<h2 id="going-further-and-calling-us-back">Going further and calling us back!</h2>
<p>If you want to have a closer look at the above example, all the <a href="https://github.com/GoogleCloudPlatform/workflows-demos/blob/master/callback-translation/translation-validation.yaml">code for this workflow</a> can be found in the <a href="https://github.com/GoogleCloudPlatform/workflows-demos/">Workflows samples</a> Github repository. And you can follow the details of this <a href="https://cloud.google.com/workflows/docs/tutorial-callbacks-firestore">tutorial</a> to replicate this workflow in your own project. As this is still a preview feature for now, please be sure to <a href="https://docs.google.com/forms/d/e/1FAIpQLSdgwrSV8Y4xZv_tvI6X2JEGX1-ty9yizv3_EAOVHWVKXvDLEA/viewform">request access to this feature</a>, if you want to try it on your own.</p>
<p>For more information on callbacks, be sure to read the <a href="https://cloud.google.com/workflows/docs/creating-callback-endpoints">documentation</a>. To dive deeper into the example above, please checkout the Github <a href="https://github.com/GoogleCloudPlatform/workflows-demos/tree/master/callback-translation">repository</a> of this translation validation sample. Don&rsquo;t hesitate to let us know via Twitter to <a href="http://twitter.com/glaforge">@glaforge</a> what you think of this feature, and how you intend on taking advantage of it in your own workflows!</p>
<p><a href="https://cloud.google.com/blog/topics/developers-practitioners/introducing-new-connectors-workflows"></a></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Skyrocketing Micronaut microservices into Google Cloud</title><link>https://glaforge.dev/talks/2021/06/25/skyrocketing-micronaut-microservices-into-google-cloud/</link><pubDate>Fri, 25 Jun 2021 14:27:21 +0100</pubDate><guid>https://glaforge.dev/talks/2021/06/25/skyrocketing-micronaut-microservices-into-google-cloud/</guid><description>&lt;p>Instead of spending too much time on infrastructure, take advantage of readily available serverless solutions. Focus on your &lt;a href="https://micronaut.io/">Micronaut&lt;/a> code, and deploy it rapidly as a function, an application, or within a container, on Google Cloud Platform,
with &lt;a href="https://cloud.google.com/functions">Cloud Functions&lt;/a>,
&lt;a href="https://cloud.google.com/appengine">App Engine&lt;/a>,
or &lt;a href="https://cloud.google.com/run">Cloud Run&lt;/a>.&lt;/p>
&lt;p>In this presentation, you’ll discover the options you have to deploy your Micronaut applications and services on Google Cloud. With &lt;a href="https://micronaut.io/launch/">Micronaut Launch&lt;/a>, it’s easy to get started with a template project, and with a few tweaks, you can then push your code to production.&lt;/p></description><content:encoded>
<![CDATA[<p>Instead of spending too much time on infrastructure, take advantage of readily available serverless solutions. Focus on your <a href="https://micronaut.io/">Micronaut</a> code, and deploy it rapidly as a function, an application, or within a container, on Google Cloud Platform,
with <a href="https://cloud.google.com/functions">Cloud Functions</a>,
<a href="https://cloud.google.com/appengine">App Engine</a>,
or <a href="https://cloud.google.com/run">Cloud Run</a>.</p>
<p>In this presentation, you’ll discover the options you have to deploy your Micronaut applications and services on Google Cloud. With <a href="https://micronaut.io/launch/">Micronaut Launch</a>, it’s easy to get started with a template project, and with a few tweaks, you can then push your code to production.</p>
<p>Thanks to its performance, its low memory consumption, and its lightning-fast startup time, Micronaut is particularly well-suited for services that run on serverless solutions.</p>
<script async class="speakerdeck-embed" data-id="d10a8e3b48244eab95ea9bd1c81547b5" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Introducing New Connectors for Workflows</title><link>https://glaforge.dev/posts/2021/04/27/introducing-new-connectors-for-workflows/</link><pubDate>Tue, 27 Apr 2021 13:52:35 +0100</pubDate><guid>https://glaforge.dev/posts/2021/04/27/introducing-new-connectors-for-workflows/</guid><description>&lt;p>&lt;a href="http://cloud.google.com/workflows">Workflows&lt;/a> is a service to orchestrate not only Google Cloud services, such as Cloud Functions,  Cloud Run, or machine learning APIs, but also external services. As you might expect from an orchestrator, Workflows allows you to define the flow of your business logic, as steps, in a YAML or JSON definition language, and provides an execution API and UI to trigger workflow executions. You can read more about the benefits of Workflows in our &lt;a href="https://cloud.google.com/blog/topics/developers-practitioners/better-service-orchestration-workflows">previous article&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://cloud.google.com/workflows">Workflows</a> is a service to orchestrate not only Google Cloud services, such as Cloud Functions,  Cloud Run, or machine learning APIs, but also external services. As you might expect from an orchestrator, Workflows allows you to define the flow of your business logic, as steps, in a YAML or JSON definition language, and provides an execution API and UI to trigger workflow executions. You can read more about the benefits of Workflows in our <a href="https://cloud.google.com/blog/topics/developers-practitioners/better-service-orchestration-workflows">previous article</a>.</p>
<p>We are happy to announce new <a href="https://cloud.google.com/workflows/docs/connectors">connectors</a> for Workflows, which simplify calling Google Cloud services and APIs. </p>
<p>The first documented connectors offered in preview when Workflows was launched in General Availability were:</p>
<ul>
<li>Cloud Tasks</li>
<li>Compute Engine</li>
<li>Firestore</li>
<li>Pub/Sub</li>
<li>Secret Manager</li>
</ul>
<p>The newly unveiled connectors are:</p>
<ul>
<li>BigQuery</li>
<li>Cloud Build</li>
<li>Cloud Functions</li>
<li>Cloud Scheduler</li>
<li>Google Kubernetes Engine</li>
<li>Cloud Natural Language API</li>
<li>Dataflow</li>
<li>Cloud SQL</li>
<li>Cloud Storage</li>
<li>Storage Transfer Service</li>
<li>Cloud Translation</li>
<li>Workflows &amp; Workflow Executions</li>
</ul>
<p>In addition to simplifying Google Cloud service calls (no need to manually tweak the URLs to call) from workflow steps, connectors also handle errors and <a href="https://cloud.google.com/workflows/docs/connectors">retries</a>, so you don&rsquo;t have to do it yourself. Furthermore, they take care of APIs with <a href="https://cloud.google.com/workflows/docs/connectors#long-running_operations">long-running operations</a>, polling the service for a result when it&rsquo;s ready, with a back-off approach, again so you don&rsquo;t have to handle this yourself.</p>
<p>Let&rsquo;s take a look at some concrete examples on how connectors help. </p>
<h2 id="creating-a-compute-engine-vm-with-a-rest-api-call">Creating a Compute Engine VM with a REST API call</h2>
<p>Imagine you want to create a Compute Engine Virtual Machine (VM) in a specified project and zone. You can do this by crafting an HTTP POST request with the proper URL, body, and OAuth2 authentication using the Compute Engine API&rsquo;s <a href="https://cloud.google.com/compute/docs/reference/rest/v1/instances/insert">instances.insert</a> method as shown in <a href="https://github.com/GoogleCloudPlatform/workflows-demos/blob/master/connector-compute/create-vm.yaml">create-vm.yaml</a>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">main</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">params</span>:<span style="color:#bbb"> </span>[args]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>- <span style="color:#062873;font-weight:bold">init</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">project</span>:<span style="color:#bbb"> </span>${sys.get_env(&#34;GOOGLE_CLOUD_PROJECT_ID&#34;)}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">zone</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;europe-west1-b&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">machineType</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;e2-small&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">instanceName</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;${args.instanceName}&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>- <span style="color:#062873;font-weight:bold">insert_machine</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>http.post<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb"> </span>${&#34;https://compute.googleapis.com/compute/v1/projects/&#34; + project + &#34;/zones/&#34; + zone + &#34;/instances&#34;}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">auth</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span>OAuth2<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">body</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">name</span>:<span style="color:#bbb"> </span>${instanceName}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">machineType</span>:<span style="color:#bbb"> </span>${&#34;zones/&#34; + zone + &#34;/machineTypes/&#34; + machineType}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">disks</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>- <span style="color:#062873;font-weight:bold">initializeParams</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span><span style="color:#062873;font-weight:bold">sourceImage</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;projects/debian-cloud/global/images/debian-10-buster-v20201112&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">boot</span>:<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">true</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">autoDelete</span>:<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">true</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">networkInterfaces</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>- <span style="color:#062873;font-weight:bold">network</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;global/networks/default&#34;</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>This works but it is quite error prone to construct the right URL with the right parameters and authentication mechanism. You also need to poll the instance status to make sure it&rsquo;s running before concluding the workflow:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">get_instance</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>http.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb"> </span>${&#34;https://compute.googleapis.com/compute/v1/projects/&#34; + project + &#34;/zones/&#34; + zone + &#34;/instances/&#34; + instanceName}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">auth</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span>OAuth2<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>getInstanceResult<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>- <span style="color:#062873;font-weight:bold">assert_running</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">switch</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">condition</span>:<span style="color:#bbb"> </span>${getInstanceResult.body.status == &#34;RUNNING&#34;}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">next</span>:<span style="color:#bbb"> </span>end<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">next</span>:<span style="color:#bbb"> </span>sleep<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>- <span style="color:#062873;font-weight:bold">sleep</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>sys.sleep<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic"># Polling through sleep</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">seconds</span>:<span style="color:#bbb"> </span><span style="color:#40a070">3</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">next</span>:<span style="color:#bbb"> </span>get_instance<span style="color:#bbb">
</span></span></span></code></pre></div><p>Note that even the HTTP GET call above could fail and it&rsquo;d be better to wrap the call in a retry logic. </p>
<h2 id="creating-a-compute-engine-vm-with-the-workflows-compute-connector">Creating a Compute Engine VM with the Workflows compute connector</h2>
<p>In contrast, let&rsquo;s now create the same VM with the compute connector dedicated to Compute Engine as shows in <a href="https://github.com/GoogleCloudPlatform/workflows-demos/blob/master/connector-compute/create-vm-connector.yaml">create-vm-connector.yaml</a>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">main</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">params</span>:<span style="color:#bbb"> </span>[args]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>- <span style="color:#062873;font-weight:bold">init</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">project</span>:<span style="color:#bbb"> </span>${sys.get_env(&#34;GOOGLE_CLOUD_PROJECT_ID&#34;)}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">zone</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;europe-west1-b&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">machineType</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;e2-small&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">instanceName</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;${args.instanceName}&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>- <span style="color:#062873;font-weight:bold">insert_machine</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>googleapis.compute.v1.instances.insert<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">project</span>:<span style="color:#bbb"> </span>${project}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">zone</span>:<span style="color:#bbb"> </span>${zone}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">body</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">name</span>:<span style="color:#bbb"> </span>${instanceName}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">machineType</span>:<span style="color:#bbb"> </span>${&#34;zones/&#34; + zone + &#34;/machineTypes/&#34; + machineType}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">disks</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>- <span style="color:#062873;font-weight:bold">initializeParams</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span><span style="color:#062873;font-weight:bold">sourceImage</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;projects/debian-cloud/global/images/debian-10-buster-v20201112&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">boot</span>:<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">true</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">autoDelete</span>:<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">true</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">networkInterfaces</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>- <span style="color:#062873;font-weight:bold">network</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;global/networks/default&#34;</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>The overall structure and syntax is pretty similar, but this time, we didn&rsquo;t have to craft the URL ourselves, nor did we have to specify the authentication method. Although it&rsquo;s invisible in this YAML declaration, error handling and retry logic are handled by Workflows directly, unlike the first example where you have to handle it yourself.</p>
<h2 id="transparent-waiting-for-long-running-operations">Transparent waiting for long-running operations</h2>
<p>Some operations from cloud services are not instantaneous and can take a while to execute. A synchronous call to such operations will return immediately with an object that indicates the status of that long-running operation. </p>
<p>From a workflow execution, you might want to call a long-running operation and move to the next step only when that operation has finished. In the standard REST approach, you have to check at regular intervals if the operation has terminated or not. To save you from the tedious work of iterating and waiting, connectors take care of this for you! </p>
<p>Let&rsquo;s illustrate this with another example with Compute Engine. Stopping a VM can take a while. A <a href="https://cloud.google.com/compute/docs/reference/rest/v1/instances/stop">request</a> to the Compute Engine REST API to stop a VM returns an <a href="https://cloud.google.com/compute/docs/reference/rest/v1/instances/stop#response-body">object</a> with a status field that indicates whether the operation has completed or not.</p>
<p>The Workflows compute connector and its <a href="https://cloud.google.com/compute/docs/reference/rest/v1/instances/stop">instances.stop</a> operation will appropriately wait for the stop of the VM &ndash; no need for you  to keep checking its status until the VM stops. It greatly simplifies your workflow definition as shown in <a href="https://github.com/GoogleCloudPlatform/workflows-demos/blob/master/connector-compute/create-stop-vm-connector.yaml">create-stop-vm-connector.yaml</a>.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">main</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">params</span>:<span style="color:#bbb"> </span>[args]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>- <span style="color:#062873;font-weight:bold">init</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">project</span>:<span style="color:#bbb"> </span>${sys.get_env(&#34;GOOGLE_CLOUD_PROJECT_ID&#34;)}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">zone</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;europe-west1-b&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">machineType</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;e2-small&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">instanceName</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;${args.instanceName}&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>...<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>- <span style="color:#062873;font-weight:bold">stop_machine</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>googleapis.compute.v1.instances.stop<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">instance</span>:<span style="color:#bbb"> </span>${instanceName}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">project</span>:<span style="color:#bbb"> </span>${project}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">zone</span>:<span style="color:#bbb"> </span>${zone}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#60a0b0;font-style:italic"># Optional connector parameters</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">connector_params</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">timeout</span>:<span style="color:#bbb"> </span><span style="color:#40a070">100</span><span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic"># total time is 100s</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">polling_policy</span>:<span style="color:#bbb">  </span><span style="color:#60a0b0;font-style:italic"># optional polling parameters for LRO polling.</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">initial_delay</span>:<span style="color:#bbb"> </span><span style="color:#40a070">1</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">multiplier</span>:<span style="color:#bbb"> </span><span style="color:#40a070">1.25</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>- <span style="color:#062873;font-weight:bold">assert_terminated</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>assert_machine_status<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">expected_status</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;TERMINATED&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">project</span>:<span style="color:#bbb"> </span>${project}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">zone</span>:<span style="color:#bbb"> </span>${zone}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">instanceName</span>:<span style="color:#bbb"> </span>${instanceName}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">assert_machine_status</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">params</span>:<span style="color:#bbb"> </span>[expected_status, project, zone, instanceName]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>- <span style="color:#062873;font-weight:bold">get_instance</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>googleapis.compute.v1.instances.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">instance</span>:<span style="color:#bbb"> </span>${instanceName}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">project</span>:<span style="color:#bbb"> </span>${project}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">zone</span>:<span style="color:#bbb"> </span>${zone}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>instance<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>- <span style="color:#062873;font-weight:bold">compare</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">switch</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">condition</span>:<span style="color:#bbb"> </span>${instance.status == expected_status}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">next</span>:<span style="color:#bbb"> </span>end<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>- <span style="color:#062873;font-weight:bold">fail</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">raise</span>:<span style="color:#bbb"> </span>${&#34;Expected VM status is &#34; + expected_status + &#34;. Got &#34; + instance.status + &#34; instead.&#34;}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Note that we still use the <a href="https://cloud.google.com/compute/docs/reference/rest/v1/instances/get">instances.get</a> operation in a subworkflow to check that the VM is indeed TERMINATED but this is nice-to-have as <a href="https://cloud.google.com/compute/docs/reference/rest/v1/instances/stop">instances.stop</a> already waits for the VM to stop before returning.</p>
<p>In connector, users can set a timeout field, which is the total wait time for this connector call. All of the retries and polling logic is hidden. Now, compare this to <a href="https://github.com/GoogleCloudPlatform/workflows-demos/blob/master/connector-compute/stop-vm.yaml">stop-vm.yaml</a> where the workflow stops the VM without the connector. You can see that the YAML is longer and the logic is more complicated with HTTP retry policy for the stop call and also the polling logic to make sure the VM is actually stopped.</p>
<h2 id="increased-reliability-through-connector-retries">Increased reliability through connector retries</h2>
<p>Even the best services can have momentary outages due to traffic spikes or network issues. Google Cloud Pub/Sub has an SLA of <a href="https://uptime.is/99.95">99.95</a>, which means no more than 43s of downtime per day on average, or under 22 minutes per month. Of course, most products routinely outperform their SLAs by a healthy margin. What if you want strong assurances your workflow won&rsquo;t fail if products remain within their SLAs? Since Workflows connectors retry operations over a period of several minutes, even if there is an outage of several minutes, the operation will succeed and so will the workflow.</p>
<h2 id="lets-connect">Let&rsquo;s connect!</h2>
<p>To learn more about <a href="https://cloud.google.com/workflows/docs/connectors">connectors</a>, have a look at some of our <a href="https://github.com/GoogleCloudPlatform/workflows-samples/tree/main/src/connectors">workflows-samples</a> repo, which show you how to interact with Compute Engine, Cloud Pub/Sub, Cloud Firestore, and Cloud Tasks. You can find the samples described in this blog post in <a href="https://github.com/GoogleCloudPlatform/workflows-demos/tree/master/connector-compute">workflows-demos/connector-compute</a>.</p>
<p>This is the initial set of connectors; there are many more Google Cloud products for which we will be creating dedicated connectors. We&rsquo;d love to hear your thoughts about which connectors we should prioritize and focus on next (fill this <a href="https://forms.gle/HKYn83bhDKWFSDQr7">form</a> to tell us). Don&rsquo;t hesitate to let us know via Twitter to <a href="https://twitter.com/meteatamel">@meteatamel</a> and <a href="http://twitter.com/glaforge">@glaforge</a>!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Serverless: panacea or not?</title><link>https://glaforge.dev/talks/2021/03/07/serverless-panacea-or-not/</link><pubDate>Sun, 07 Mar 2021 22:36:12 +0100</pubDate><guid>https://glaforge.dev/talks/2021/03/07/serverless-panacea-or-not/</guid><description>&lt;p>At &lt;a href="https://www.devday.be/Sessions/Details/94?slug=the-serverless-panacea-or-not">DevDay Belgium&lt;/a>, a few months ago, I had the pleasure to give a keynote on the theme of &amp;ldquo;serverless&amp;rdquo;. Let me share with you this talk today!&lt;/p>
&lt;p>&lt;strong>The Serverless Panacea&amp;hellip; Or Not?&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>The term &amp;ldquo;serverless&amp;rdquo; has become a trendy buzzword: if you don&amp;rsquo;t have the checkbox ticked, you&amp;rsquo;re not cool anymore. Really?&lt;/p>
&lt;p>Spoiler alert: There may be servers involved in serverless solutions. It&amp;rsquo;s not just about function-as-a-service. And it&amp;rsquo;s actually more complicated than it may seem!&lt;/p></description><content:encoded>
<![CDATA[<p>At <a href="https://www.devday.be/Sessions/Details/94?slug=the-serverless-panacea-or-not">DevDay Belgium</a>, a few months ago, I had the pleasure to give a keynote on the theme of &ldquo;serverless&rdquo;. Let me share with you this talk today!</p>
<p><strong>The Serverless Panacea&hellip; Or Not?</strong></p>
<blockquote>
<p>The term &ldquo;serverless&rdquo; has become a trendy buzzword: if you don&rsquo;t have the checkbox ticked, you&rsquo;re not cool anymore. Really?</p>
<p>Spoiler alert: There may be servers involved in serverless solutions. It&rsquo;s not just about function-as-a-service. And it&rsquo;s actually more complicated than it may seem!</p>
<p>But first, let&rsquo;s come back to the basics: what is serverless exactly, where does it come from, what are its characteristics? Then, beyond the definition, we&rsquo;ll discuss the challenges, and the risks associated with serverless architectures. Eventually, going further, we&rsquo;ll think about where serverless is heading in the near future.</p></blockquote>
<p>You can find the slides below:</p>
<script async class="speakerdeck-embed" data-id="cb3b04200d7b4b94a0b22dfd9a401d0a" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>And you can find the video below. Further down, I&rsquo;ll detail each slide of my keynote:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/EkE3UfzVO8o?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>Today, I&rsquo;d like to tell you about Serverless.
As a Developer Advocate, for Google Cloud, that&rsquo;s products and the topic I&rsquo;m focusing on.
Serverless is a big buzzword of the day, but is it a real panacea or not?</p>
<p>Like Obelix, I fell into the magic serverless potion a long time ago&hellip;
I started playing with Google App Engine Java in 2009, even before it was officially announced by Google at the Google I/O conference.
Google team reached out to me, to work together in stealth mode, to ensure that alternative JVM languages would run fine on their upcoming Java flavor for App Engine (I&rsquo;m the co-founder of the Groovy language)</p>
<p>I couldn&rsquo;t imagine then that I&rsquo;d be starting to work for Google 7 years later. And that I would focus on those serverless solutions!
It was still called Platform-as-a-Service, as the term &ldquo;serverless&rdquo; wasn&rsquo;t invented yet (although they are pretty similar)
And I&rsquo;ve been a big fan and big user of App Engine ever since.</p>
<p>After this brief personal story with Obelix and Google, let&rsquo;s actually start with a little bit of background and history.
This is my version of the story, so don&rsquo;t take it too seriously.</p>
<p>At the beginning, humans created the server. 
A machine on which you could run various programs and apps.
Well, we also created the internet, of course, otherwise we couldn&rsquo;t connect our web apps to our users.
If you have a few users, a single server may suffice.
But you know how it goes, with the spread of the web and internet, we now have billions of users, and millions of servers.
Things kinda got complicated, and we introduced lots of hard concepts around distributed microservices, replicated databases.
We even coined theorems, like the CAP theorem, for Consistency, Availability, Partitioning. But you can only pick 2.</p>
<p>Humans invented the cloud, in order to avoid dealing with the physical world.
But there are still databases, servers, or virtual machines to manage.
However, you don&rsquo;t have to get your hands dirty with the ethernet cables, changing the failing hard-drives, upgrading to the latest CPU and RAM.
Usually, it&rsquo;s the cloud provider that has folks that wake up in the middle of the night to upgrade those things.
You can sleep a little bit better at night, even if your boss may still call you at 3am because your app is misbehaving.</p>
<p>To focus on the code, and to avoid the complexity of managing servers, provisioning clusters, configuring networking, fine-tuning databases, even in the cloud&hellip; humans came up with the concept of serverless! 
Here is a picture of the latest Google datacenter for serverless! Look, no servers!
Well, I&rsquo;m kidding, of course, there are always servers around!</p>
<p>Even if the word serverless wasn&rsquo;t there yet, it all started with Platform-as-a-Service, with App Engine and Heroku.
The promise was &ldquo;give us your code or your app, and we&rsquo;ll run it for you&rdquo;.
The hardware management aspect was already there. Scaling was also handled by the platform.
The pricing as well was proportional to the usage of the resources.</p>
<p>You also have BaaS &mdash; Backend as a Service
It&rsquo;s pretty similar to PaaS actually.
It comes with batteries-included. You focus on the frontend, and all the backend is provided for you.
Parse and Firebase are two good examples. Facebook kinda abandoned Parse into open-source land.
But Firebase is still around and integrates more and more with the various Google Cloud Platform services.
So you can have hosting of static assets, a datastore to save your information, some kind of runtime environment to run your business logic.
And tons of other services, for authentication, mobile crash analysis, performance testing, analytics, and more.</p>
<p>PaaS, then BaaS, and also FaaS: Functions-as-a-service.
With a FaaS solution, your unit of work, of deployment, becomes a small, granular function.
This concept was popularized by Amazon Lambda.
And often, even still today, people tend to confuse FaaS with Serverless.
But FaaS is really just one facet of the Serverless ecosystem. Like PaaS or BaaS.</p>
<p>Another interesting facet of serverless is the Container-as-a-Service approach, with containerized workloads.
Instead deploying apps or functions, you&rsquo;re deploying containers.
Put anything you want inside a container. 
That&rsquo;s the approach that Google took with its Cloud Run container service.
You don&rsquo;t have the complexity of Kubernetes, but you can run your container easily in the cloud, in a fully managed environment.</p>
<p>Right, so I described a bit the history that lead us to serverless and its various facets, and some of the serverless products that are available today, but let&rsquo;s take some time to give a proper definition of what serverless is.</p>
<p>For me, Serverless is the easiest way to get an idea to production in a minimal amount of time.
As a developer, you work on some code, and then you deploy it. That&rsquo;s it! Really!</p>
<p>The term serverless was coined around 2010 by someone called Ken Elkabany, who created the PiCloud computing platform.
Compared to Heroku and App Engine which came earlier, and focusing on implementing web stacks in their cloud datacenters, PiCloud was more generic and supported different kinds of workloads, not just serving web requests.
The catchy term came from the fact that they were actually selling a service, rather than selling or renting servers, machines, VMs, to their customers.</p>
<p>There are 2 ways to think about Serverless: there&rsquo;s the Operational model, and the Programming model.</p>
<p>Operational model:</p>
<ul>
<li>Fully managed, </li>
<li>Automatic scaling, </li>
<li>Pay as you go</li>
</ul>
<p>Programming model</p>
<ul>
<li>Service based, </li>
<li>Event driven, </li>
<li>Stateless</li>
</ul>
<p>There&rsquo;s no provisioning of clusters, servers, instances, VMs or anything.
It&rsquo;s all handled by the platform, for you. Just give your code, your app, your function.
It&rsquo;s a fully managed environment. Security patches are applied automatically. 
You remember specter and meltdown recently? They&rsquo;ve been mitigated transparently and rapidly for customers by Google Cloud. No wake up call in the night.
Your apps will scale automatically, from 0 to 1 instance, from 1 to n instances, and from n down to 1, as well as back to zero.
Tracking the CPU load, memory usage, number of incoming requests, with some magic formula, serverless platforms are able to scale up and down your services.
Without you having to worry about it. The cloud provider is handling that for you.</p>
<p>In terms of pricing, it&rsquo;s a Pay-as-you-go cost model.
It goes hand in hand with automatic scaling.
If there&rsquo;s no traffic, you pay zero.
If there&rsquo;s twice more traffic than usual, you pay proportionately.
And if the load goes back to zero, the instances serving your app are decommissioned, and again you pay zero.</p>
<p>Now onto the programming model.
More and more, we&rsquo;re transitioning from building big monoliths into orchestrating smaller services, or microservices.
It has its challenges though, but with smaller services, your teams can develop them more independently, scale them differently, or event deploy them with different life cycles.</p>
<p>Since you have some more loosely coupled services, they tend to react to incoming events from your system or from the cloud (for example a notification of a new file in cloud storage, a new line in a reactive datastore like Cloud Firestore, a message in a message bus like Pub/Sub), 
Your services usually communicate asynchronously, to stay properly decoupled.
But the more asynchronous you are, the harder things are to operate and monitor, when business logic spans several services, you have to figure out what&rsquo;s the current status of that workflow across those services.</p>
<p>Another important aspect of the fact that services can scale up and down and back to zero is that there&rsquo;s no guarantee that you&rsquo;re going to hit the same server all the time. 
So you can&rsquo;t be certain that some data that would be cached is still there.
You have to program defensively to ensure that any fresh instance of your app is able to cope with any incoming request.
State is pretty much an enemy of scaling. So the more stateless you can be, the better it is.</p>
<ul>
<li>Compute, </li>
<li>Data Analytics</li>
<li>ML &amp; AI</li>
<li>Database &amp; Storage</li>
<li>Smart assistants &amp; chat</li>
<li>DevOps</li>
<li>Messaging</li>
</ul>
<p>We&rsquo;ve been speaking about serverless compute, but serverless is not just about compute.
You could consider that anything that is fully managed, that offers a pay-as-you-go cost model, that is a service in the cloud, then it&rsquo;s also all serverless, since you don&rsquo;t have to worry about the infrastructure and the scaling.
There are great examples of this in Google Cloud, for example BigQuery which is a fully-managed, serverless data warehouse and analytics platform. You pay proportionally to the amount of data your queries are going through! Not for the storage, not for the running servers, etc. 
But let&rsquo;s get back to serverless compute.</p>
<p>Serverless sounds pretty cool, right?
But there are also challenges, compared to running a good old monolith on your on-premises server.
We&rsquo;ve already given some hints of some of the challenges.
In particular, I&rsquo;d like to spend some time to tell you about four key aspects:</p>
<ul>
<li>The lock-in factor</li>
<li>The infamous cold starts</li>
<li>Cost controls</li>
<li>And the mess of spaghetti microservices</li>
</ul>
<p>PaaS or BaaS often come with batteries-included.
They have built-in APIs or databases, which are super convenient for developers.
As a developer, you are much more productive, because you don&rsquo;t have to wire things up, configure third-party services. The choice is already made for you.
But I&rsquo;m seeing those batteries more and more being externalized, as their own standalone products. 
Google Cloud has externalized things like its NoSQL database, its Pub/Sub message hub, its scheduler, its task handling capabilities. Before those services were actually part of the Platform-as-a-Service.</p>
<p>However great having built-in powerful batteries is, often these are proprietary and specific to that platform.
You end up being locked to the platform you&rsquo;re building upon.
It can be a choice, as long as you are aware of it.
It&rsquo;s a trade-off between portability and time-to-market.
You might still be tied to those products, but at least, you can still move the business logic around, if those services are externalized. 
And you can create a level of indirection to be able, some day, potentially, to move away from those service dependencies if needed.</p>
<p>A common issue you hear about in serverless-land is the infamous &ldquo;Cold Start&rdquo;.
Since you can scale to zero, it means there&rsquo;s currently no server, instance, or clone, to serve an incoming request.
So what happens? The cloud provider has to reinitialize, re-instantiate, re-hydrate some kind of server, VM, or container.
Additionally, the underlying language runtime has to startup as well, initializing its internal data structures.
Not only that, but your app also needs to get started too.
So you&rsquo;d better try to minimize the time your apps need to be ready to serve their first request. Since you have control over this part.</p>
<p>There are workarounds, like pinging your service at regular intervals to keep it warm, but it&rsquo;s a bit of a hack, or even an anti-pattern.
Depending on the pricing, that might mean you&rsquo;re paying for nothing potentially, for something that&rsquo;s sitting idle.
Some platforms provide some knobs that you can play with like &ldquo;min instances&rdquo; or &ldquo;provisioned instances&rdquo;, usually at a lower price.
For instance, on Google Cloud Functions or Cloud Run, you can specify that you want a certain minimum number of instances that are already warm, and ready to serve, and that are cheaper.</p>
<p>I mention a minimum number of instances, but what about the notion of maximum number of instances?
It&rsquo;s actually an important idea. 
With a platform that auto-scales transparently, that can spin up as many instances to serve increased traffic, it also means that your costs are going to increase just as much! 
So in order to bound your budget to a known quantity, rather than burning your money with all your hot instances, you can cap the number of instances that will serve your content. The service may be a bit degraded when you reach that limit, as latency will likely increase, but at least, your budget doesn&rsquo;t go through the roof!
That&rsquo;s why Google Cloud Platform introduced that notion of capping the number of instances running your functions, apps or containers in its serverless product: to have more visibility and control around costs.</p>
<p>The last challenge I&rsquo;d like to mention is spaghetti services.
It&rsquo;s so easy to write many functions and services on a serverless platform.
One service does one thing and does it well, right?
But after a while, you end up with a spaghetti of microservices. A big mess.
It becomes very complicated to see what invokes what. 
Hard for monitoring and observability to really figure out what happened, when one microservice starting somehow to misbehave completely ruin your clockwork architecture.
And you know: monoliths aren&rsquo;t that bad, actually.
Don&rsquo;t start right away with writing the smallest unit of work possible. 
Pay attention to how you split the big monolith into microservices.
Otherwise, you&rsquo;ll end up with that big plate of spaghetti. 
There are good articles on when and how to split monolith, but it&rsquo;s not a simple rule of thumb answer.</p>
<p>So what does the future hold for serverless?
I believe that the highlights will be about:</p>
<ul>
<li>Openness</li>
<li>Containers</li>
<li>Glue</li>
<li>Edge</li>
<li>Machine Learning</li>
</ul>
<p>Let&rsquo;s start with open, and openness. That&rsquo;s open like in open source!
We want to avoid lock-in. We want portability.
For instance, the platforms rely on open source software for sure, but the platforms themselves can be open source too.
If you look at Google&rsquo;s Cloud Run, it&rsquo;s actually based on the Knative open source Kubernetes platform.
So you&rsquo;re not locked in Google Cloud when you&rsquo;re using Cloud Run. You can move your workload, your app, on a Knative compatible platform from another cloud provider, or even on-premises, on your own infrastructure.
I worked on the Java Cloud Functions runtime, and it is also available as open source. So you can deploy your functions in Google Cloud, but you can also run your functions elsewhere too, in a hybrid cloud scenario, or even just locally on your machine for greater developer experience and a tighter development feedback loop.
Also, the way you build your services from sources, this can be made more open too. 
For instance, Heroku and Google Cloud partnered together on Cloud Native Buildpacks, it helps you transform your application source code into images that can run on any cloud.
Really, it&rsquo;s all about portability and avoiding lock-in, by making things as open as possible.</p>
<p>As I&rsquo;m mentioning Cloud Native Buildpacks, and the fact it builds portable containers for your app&rsquo;s source code, notice that we&rsquo;re speaking of containers.
Why containers, you may ask. 
With things like platform or function as a service, you are pushing apps on the platform runtime. But you may be limited in terms of language runtime, or library, or binary that you can run there or bundle. If you&rsquo;re using an esoteric language, or need some special software installed, perhaps you won&rsquo;t be able to run your app there.
Instead, if you could put everything you need in a box, and if the cloud could just run that box for you, then you can do pretty much anything.
That&rsquo;s why we&rsquo;re using containers more and more. And that&rsquo;s also why Google Cloud released Cloud Run, to run your containers, but serverlessly, with any runtime, library or language that you want, without limitations.
So I&rsquo;m seeing more containers in the future.</p>
<p>You remember my plate of spaghettis? 
To orchestrate your services, to observe and monitor them, to track that they are communicating properly, asynchronously, you&rsquo;ll need more tools to ensure that it all runs fine in the cloud. That&rsquo;s why I&rsquo;m seeing more tools like Google Cloud Tasks, Cloud Scheduler, Cloud Workflows, and in the Azure and AWS worlds, you have things like Logic App or Step Functions.
You also have various messaging busses, like Google Cloud Pub/Sub, Amazon SQS, Azure Service Bus.
And in the Kubernetes world, we&rsquo;ve seen service meshes emerge as a key architectural pattern.
A monolith is much simpler to develop &amp; operate, but as you move to a microservice architecture, those glue services will be needed more and more.
So I see more glue in the future!</p>
<p>Recently, CloudFlare released a product called CloudFlare Workers.
It&rsquo;s using the V8 JavaScript engine, and its isolates concept to run your functions, in a sandboxed manner.
There are two very interesting aspects to me in this new product.
First of all, that&rsquo;s the idea of having your serverless functions run at the edge of the network. 
Not deep in a handful of big data centers. Instead, those functions are as close to the users as possible.
So the latency is really minimal.
Secondly, to further reduce latency, there&rsquo;s a great innovation that almost completely eliminates cold starts!
CloudFlare actually starts warming up your function as soon as the SSL handshake is requested when you invoke the function via HTTPS, although normally the whole handshake operation has to be done first, and the call routed to your function, before really starting.
So that&rsquo;s a really great optimization! And we&rsquo;ll probably see more stuff moving to the edge of the cloud infrastructure.</p>
<p>Lastly, looking even further in the future, I&rsquo;m curious to see how machine learning will play a role in the serverless offering of cloud providers.
In particular, you still have to specify a VM or instance size, its memory or CPU. Some would say it&rsquo;s not very serverless, since servers are supposed to be abstracted away.
In Google Cloud, for example, we have what we call a &ldquo;clone scheduler&rdquo; that is responsible for creating a new instance of your function or app, depending on various factors, like CPU usage, memory usage, number of incoming queries, etc. 
There&rsquo;s some magical calculation that figures out how and when to spin up a new instance.</p>
<p>Google recently automated its datacenter thanks to Machine Learning, reducing its power consumption by 40%! (Power Usage Efficiency)
I can imagine a future where Machine Learning is used to further upsize or downsize the underlying machines running your serverless code, and provision the right amount of resources, to reduce latency, CPU usage, etc.
So let&rsquo;s see what the future holds for Serverless!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Orchestrating the Pic-a-Daily serverless app with workflows</title><link>https://glaforge.dev/posts/2021/02/13/orchestrating-the-pic-a-daily-serverless-app-with-workflows/</link><pubDate>Sat, 13 Feb 2021 19:15:05 +0100</pubDate><guid>https://glaforge.dev/posts/2021/02/13/orchestrating-the-pic-a-daily-serverless-app-with-workflows/</guid><description>&lt;p>Over the past year, we (&lt;a href="https://twitter.com/meteatamel">Mete&lt;/a> and &lt;a href="https://twitter.com/glaforge">Guillaume&lt;/a>) have developed a picture sharing application, named Pic-a-Daily, to showcase Google Cloud serverless technologies such as Cloud Functions, App Engine, and Cloud Run. Into the mix, we&amp;rsquo;ve thrown a pinch of Pub/Sub for interservice communication, a zest of Firestore for storing picture metadata, and a touch of machine learning for a little bit of magic.&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-67de80a884cfbad321d40341bd2b97ab">
&lt;img src="https://storage.googleapis.com/gweb-cloudblog-publish/images/1_Shqfx7L.max-1400x1400.png"
alt="/img/picadailly-workflows/1_Shqfx7L.max-1400x1400.png"
/>
&lt;/a>
&lt;figcaption>/img/picadailly-workflows/1_Shqfx7L.max-1400x1400.png&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-67de80a884cfbad321d40341bd2b97ab">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://storage.googleapis.com/gweb-cloudblog-publish/images/1_Shqfx7L.max-1400x1400.png"
alt="/img/picadailly-workflows/1_Shqfx7L.max-1400x1400.png"
/>
&lt;div class="lightbox-caption">/img/picadailly-workflows/1_Shqfx7L.max-1400x1400.png&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>We also created a &lt;a href="https://codelabs.developers.google.com/serverless-workshop/">hands-on workshop&lt;/a> to build the application, and &lt;a href="https://speakerdeck.com/meteatamel/pic-a-daily-serverless-workshop">slides&lt;/a> with explanations of the technologies used. The workshop consists of codelabs that you can complete at your own pace. All the code is open source and available in a &lt;a href="https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop">GitHub repository&lt;/a>. &lt;/p></description><content:encoded>
<![CDATA[<p>Over the past year, we (<a href="https://twitter.com/meteatamel">Mete</a> and <a href="https://twitter.com/glaforge">Guillaume</a>) have developed a picture sharing application, named Pic-a-Daily, to showcase Google Cloud serverless technologies such as Cloud Functions, App Engine, and Cloud Run. Into the mix, we&rsquo;ve thrown a pinch of Pub/Sub for interservice communication, a zest of Firestore for storing picture metadata, and a touch of machine learning for a little bit of magic.</p>
<p><figure>
  <a href="#img-67de80a884cfbad321d40341bd2b97ab">
    <img src="https://storage.googleapis.com/gweb-cloudblog-publish/images/1_Shqfx7L.max-1400x1400.png"
      alt="/img/picadailly-workflows/1_Shqfx7L.max-1400x1400.png"
       />
  </a>
  <figcaption>/img/picadailly-workflows/1_Shqfx7L.max-1400x1400.png</figcaption>
</figure>
<div class="lightbox" id="img-67de80a884cfbad321d40341bd2b97ab">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://storage.googleapis.com/gweb-cloudblog-publish/images/1_Shqfx7L.max-1400x1400.png"
    alt="/img/picadailly-workflows/1_Shqfx7L.max-1400x1400.png"
     />
  <div class="lightbox-caption">/img/picadailly-workflows/1_Shqfx7L.max-1400x1400.png</div>
</div>
</p>
<p>We also created a <a href="https://codelabs.developers.google.com/serverless-workshop/">hands-on workshop</a> to build the application, and <a href="https://speakerdeck.com/meteatamel/pic-a-daily-serverless-workshop">slides</a> with explanations of the technologies used. The workshop consists of codelabs that you can complete at your own pace. All the code is open source and available in a <a href="https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop">GitHub repository</a>. </p>
<h2 id="initial-event-driven-architecture">Initial event-driven architecture</h2>
<p>The Pic-a-Daily application evolved progressively. As new services were added over time, a loosely-coupled, event-driven architecture naturally emerged, as shown in this architecture diagram:</p>
<p><figure>
  <a href="#img-eb1f8c5e07f687a263764ee54504284b">
    <img src="https://storage.googleapis.com/gweb-cloudblog-publish/images/2_XMbrwvr.max-1100x1100.png"
      alt="/img/picadailly-workflows/2_XMbrwvr.max-1100x1100.png"
       />
  </a>
  <figcaption>/img/picadailly-workflows/2_XMbrwvr.max-1100x1100.png</figcaption>
</figure>
<div class="lightbox" id="img-eb1f8c5e07f687a263764ee54504284b">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://storage.googleapis.com/gweb-cloudblog-publish/images/2_XMbrwvr.max-1100x1100.png"
    alt="/img/picadailly-workflows/2_XMbrwvr.max-1100x1100.png"
     />
  <div class="lightbox-caption">/img/picadailly-workflows/2_XMbrwvr.max-1100x1100.png</div>
</div>
</p>
<p>To recap the event-driven flow:</p>
<ol>
<li>Users upload pictures on an App Engine web frontend. Those pictures are stored in a Google Cloud Storage bucket, which triggers file creation and deletion events, propagated through mechanisms such as Pub/Sub and Eventarc. </li>
<li>A Cloud Function (Image analysis) reacts to file creation events. It calls the Vision API to assign labels to the picture, identify the dominant colors, and check if it&rsquo;s a picture safe to show publicly. All this picture metadata is stored in Cloud Firestore. </li>
<li>A Cloud Run service (Thumbnail service) also responds to file creation events. It generates thumbnails of the high-resolution images and stores them in another bucket. </li>
<li>On a regular schedule triggered by Cloud Scheduler, another Cloud Run service (Collage services) creates a collage from thumbnails of the four most recent pictures. </li>
<li>Last but not least, a third Cloud Run service (Image garbage collector) responds to file deletion events received through <a href="https://cloud.google.com/blog/products/serverless/eventarc-is-ga">(recently generally available) Eventarc</a>. When a high-resolution image is deleted from the pictures bucket, this service deletes the thumbnail and the Firestore metadata of the image.</li>
</ol>
<p>These services are loosely coupled and take care of their own logic, in a smooth choreography of events. They can be scaled independently. There&rsquo;s no single point of failure, since services can continue to operate even if others have failed. Event-based systems can be extended beyond the current domain at play by plugging in other events and services to respond to them.</p>
<p>However, monitoring such a system in its entirety usually becomes complicated, as there&rsquo;s no centralized place to see where we&rsquo;re at in the current business process that spans all the services. Speaking of business processes, it&rsquo;s harder to capture and make sense of the flow of events and the interplay between services. Since there&rsquo;s no global vision of the processes, how do we know if a particular process or transaction is successful or not? And when failures occur, how do we deal properly and explicitly with errors, retries, or timeouts?</p>
<p>As we kept adding more services, we started losing sight of the underlying &ldquo;business flow&rdquo;. It became harder to isolate and debug problems when something failed in the system. That&rsquo;s why we decided to investigate an orchestrated approach.</p>
<h2 id="orchestration-with-workflows">Orchestration with Workflows</h2>
<p><a href="https://cloud.google.com/workflows">Workflows</a> recently became generally available. It offered us a great opportunity to re-architect our application and use an orchestration approach, instead of a completely event-driven one. In orchestration, instead of microservices responding to events, there is an external service, such as Workflows, calling microservices in a predefined order. </p>
<p>After some restructuring, the following architecture emerged with Workflows:</p>
<p><figure>
  <a href="#img-3e3549aeed71a6e5e1b01e831909af73">
    <img src="https://storage.googleapis.com/gweb-cloudblog-publish/images/3_temY387.max-1000x1000.png"
      alt="/img/picadailly-workflows/3_temY387.max-1000x1000.png"
       />
  </a>
  <figcaption>/img/picadailly-workflows/3_temY387.max-1000x1000.png</figcaption>
</figure>
<div class="lightbox" id="img-3e3549aeed71a6e5e1b01e831909af73">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://storage.googleapis.com/gweb-cloudblog-publish/images/3_temY387.max-1000x1000.png"
    alt="/img/picadailly-workflows/3_temY387.max-1000x1000.png"
     />
  <div class="lightbox-caption">/img/picadailly-workflows/3_temY387.max-1000x1000.png</div>
</div>
</p>
<p>Let&rsquo;s recap the orchestrated approach:</p>
<ul>
<li>App Engine is still the same web frontend that accepts pictures from our users and stores them in the Cloud Storage bucket. </li>
<li>The file storage events trigger two functions, one for the creation of new pictures and one for the deletion of existing pictures. Both functions create a workflow execution. For file creation, the workflow directly makes the call to the Vision API (declaratively instead of via Cloud Function code) and stores picture metadata in Firestore via its REST API. </li>
<li>In between, there&rsquo;s a function to transform the useful information of the Vision API into a document to be stored in Firestore. Our initial image analysis function has been simplified: The workflow makes the REST API calls and only the data transformation part remains. </li>
<li>If the picture is safe to display, the workflow saves the information in Firestore, otherwise, that&rsquo;s the end of the workflow. </li>
<li>This branch of the workflow ends with calls to Thumbnail and Collage Cloud Run services. This is similar to before, but with no Pub/Sub or Cloud Scheduler to set up. </li>
<li>The other branch of the workflow is for the picture garbage collection. The service itself was completely removed, as it mainly contained API calls without any business logic. Instead, the workflow makes these calls. </li>
</ul>
<p>There is now a central <a href="https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop/blob/master/workflows/workflows.yaml">workflows.yaml</a> file capturing the business flow. You can also see a visualization of the flow in Cloud Console:</p>
<p><figure>
  <a href="#img-7fac4aae5126c606b82c0f05a21c7ba2">
    <img src="https://storage.googleapis.com/gweb-cloudblog-publish/images/4_zpsaVq7.max-1500x1500.png"
      alt="/img/picadailly-workflows/4_zpsaVq7.max-1500x1500.png"
       />
  </a>
  <figcaption>/img/picadailly-workflows/4_zpsaVq7.max-1500x1500.png</figcaption>
</figure>
<div class="lightbox" id="img-7fac4aae5126c606b82c0f05a21c7ba2">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://storage.googleapis.com/gweb-cloudblog-publish/images/4_zpsaVq7.max-1500x1500.png"
    alt="/img/picadailly-workflows/4_zpsaVq7.max-1500x1500.png"
     />
  <div class="lightbox-caption">/img/picadailly-workflows/4_zpsaVq7.max-1500x1500.png</div>
</div>
</p>
<p>The Workflows UI shows which executions failed, at which step, so we can see which one had an issue without having to dive through heaps of logs to correlate each service invocation. Workflows also ensures that each service call completes properly, and it can apply global error and retry policies.</p>
<p>With orchestration, the business flows are captured more centrally and explicitly, and can even be version controlled. Each step of a workflow can be monitored, and errors, retries, and timeouts can be laid out clearly in the workflow definition. When using Cloud Workflows in particular, services can be called directly via REST, instead of relying on events on Pub/Sub topics. Furthermore, all the services involved in those processes can remain independent, without knowledge of what other services are doing.</p>
<p>Of course, there are downsides as well. If you add an orchestrator into the picture, you have one more component to worry about, and it could become the single point of failure of your architecture (fortunately, Google Cloud products come with SLAs!). Last, we should mention that relying on REST endpoints might potentially increase coupling, with a heavier reliance on strong payload schemas vs lighter events formats.</p>
<h2 id="lessons-learned">Lessons learned</h2>
<p>Working with Workflows was refreshing in a number of ways and offered us some lessons that are worth sharing. </p>
<h3 id="better-visibility">Better visibility</h3>
<p>It is great to have a high-level overview of the underlying business logic, clearly laid out in the form of a <a href="https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop/blob/master/workflows/workflows.yaml">YAML declaration</a>. Having visibility into each workflow execution was useful, as it enabled us to clearly understand what worked in each execution, without having to dive into the logs to correlate the various individual service executions.</p>
<h3 id="simpler-code">Simpler code</h3>
<p>In the original event-driven architecture, we had to deal with three types of events:</p>
<ol>
<li>Cloud Functions&rsquo; direct integration with Cloud Storage events</li>
<li>HTTP wrapped Pub/Sub messages with Cloud Storage events for Cloud Run</li>
<li>Eventarc&rsquo;s CloudEvents based Cloud Storage events for Cloud Run</li>
</ol>
<p>As a result, the code had to cater to each flavor of events:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Cloud Functions provides the event directly
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>exports.vision_analysis <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">async</span> (event, context) =&gt; {
</span></span><span style="display:flex;"><span> <span style="color:#007020;font-weight:bold">const</span> filename <span style="color:#666">=</span> event.name;
</span></span><span style="display:flex;"><span> <span style="color:#007020;font-weight:bold">const</span> filebucket <span style="color:#666">=</span> event.bucket;
</span></span><span style="display:flex;"><span> ...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Cloud Run encodes the GCS event in Base64 in a Pub/Sub message
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// and wraps the message in an HTTP request
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>app.post(<span style="color:#4070a0">&#39;/&#39;</span>, <span style="color:#007020;font-weight:bold">async</span> (req, res) =&gt; {
</span></span><span style="display:flex;"><span> <span style="color:#007020;font-weight:bold">const</span> pubSubMessage <span style="color:#666">=</span> req.body;
</span></span><span style="display:flex;"><span> <span style="color:#007020;font-weight:bold">const</span> eventType <span style="color:#666">=</span> pubSubMessage.message.attributes.eventType;
</span></span><span style="display:flex;"><span> <span style="color:#007020;font-weight:bold">const</span> fileEvent <span style="color:#666">=</span> JSON.parse(
</span></span><span style="display:flex;"><span> Buffer.from(pubSubMessage.message.data, <span style="color:#4070a0">&#39;base64&#39;</span>)
</span></span><span style="display:flex;"><span> .toString().trim());
</span></span><span style="display:flex;"><span> ...
</span></span><span style="display:flex;"><span><span style="">​</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Eventarc encodes events with CloudEvents
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// and Cloud Run wraps it in an HTTP request
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>app.post(<span style="color:#4070a0">&#39;/&#39;</span>, <span style="color:#007020;font-weight:bold">async</span> (req, res) =&gt; {
</span></span><span style="display:flex;"><span> <span style="color:#007020;font-weight:bold">const</span> cloudEvent <span style="color:#666">=</span> HTTP.toEvent({
</span></span><span style="display:flex;"><span> headers<span style="color:#666">:</span> req.headers, body<span style="color:#666">:</span> req.body });
</span></span><span style="display:flex;"><span> <span style="color:#007020;font-weight:bold">const</span> tokens <span style="color:#666">=</span> logEntryData.protoPayload.resourceName.split(<span style="color:#4070a0">&#39;/&#39;</span>);
</span></span><span style="display:flex;"><span> <span style="color:#007020;font-weight:bold">const</span> bucket <span style="color:#666">=</span> tokens[<span style="color:#40a070">3</span>];
</span></span><span style="display:flex;"><span> <span style="color:#007020;font-weight:bold">const</span> objectName <span style="color:#666">=</span> tokens[<span style="color:#40a070">5</span>];
</span></span><span style="display:flex;"><span> ...
</span></span></code></pre></div><p>In the orchestrated version, there&rsquo;s only a simple REST call and HTTP POST body to parse:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Workflows calls services directly,
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// No events to unwrap
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>app.post(<span style="color:#4070a0">&#39;/&#39;</span>, <span style="color:#007020;font-weight:bold">async</span> (req, res) =&gt; {
</span></span><span style="display:flex;"><span> <span style="color:#60a0b0;font-style:italic">// gs://picture-bucket/image.jpg
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span> <span style="color:#007020;font-weight:bold">const</span> gcsImageUri <span style="color:#666">=</span> req.body.gcsImageUri;
</span></span><span style="display:flex;"><span> <span style="color:#007020;font-weight:bold">const</span> tokens <span style="color:#666">=</span> gcsImageUri.substr(<span style="color:#40a070">5</span>).split(<span style="color:#4070a0">&#39;/&#39;</span>);
</span></span><span style="display:flex;"><span> <span style="color:#007020;font-weight:bold">const</span> fileEvent <span style="color:#666">=</span> { bucket<span style="color:#666">:</span> tokens[<span style="color:#40a070">0</span>], name<span style="color:#666">:</span> tokens[<span style="color:#40a070">1</span>] };
</span></span></code></pre></div><h3 id="less-code">Less code</h3>
<p>Moving REST calls into the workflow definition as a declaration (with straightforward authentication) enabled us to eliminate quite a bit of code in our services; one service was trimmed down into a simple data transformation function, and another service completely disappeared! Two functions for triggering two paths in the workflow were needed though, but with a future integration with Eventarc, they may not be required anymore. </p>
<h3 id="less-setup">Less setup</h3>
<p>In the original event-driven architecture, we had to create Pub/Sub topics, and set up Cloud Scheduler and Eventarc to wire-up services. With Workflows, all of this setup is gone. <a href="https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop/blob/master/workflows/workflows.yaml">Workflows.yaml</a> is the single source of setup needed for the business flow. </p>
<h3 id="error-handling">Error handling</h3>
<p>Error handling was also simplified in a couple of ways. First, the whole flow stops when an error occurs, so we were no longer in the dark about exactly which services succeeded and which failed in our chain of calls. Second, we now have the option of applying global error and retry policies. </p>
<h3 id="learning-curve">Learning curve</h3>
<p>Now, everything is not always perfect! We had to learn a new service, with its quirks and limited documentation &mdash; it&rsquo;s still early, of course, and the documentation will improve over time with feedback from our customers.</p>
<h3 id="code-vs-yaml">Code vs. YAML </h3>
<p>As we were redesigning the architecture, an interesting question came up over and over: &ldquo;Should we do this in code in a service or should we let Workflows make this call from the YAML definition?&rdquo;</p>
<p>In Workflows, more of the logic lands in the workflow definition file in YAML, rather than code in a service. Code is usually easier to write, test, and debug than YAML, but it also requires more setup and maintenance than a step definition in Workflows.</p>
<p>If it&rsquo;s boilerplate code that simply makes a call to some API, that should be turned into YAML declarations. However, if the code also has extra logic, then it&rsquo;s probably better to leave it in code, as YAML is less testable. Although there is some level of error reporting in the Workflows UI, it&rsquo;s not a full-fledged IDE that helps you along the way. Even when working in your IDE on your development machine, you&rsquo;ll have limited help from the IDE, as it only checks for valid YAML syntax.</p>
<h3 id="loss-of-flexibility">Loss of flexibility</h3>
<p>The last aspect we&rsquo;d like to mention is perhaps a loss of flexibility. Working with a loosely-coupled set of microservices that communicate via events is fairly extensible, compared to a more rigid solution that mandates a strict definition of the business process descriptions.</p>
<h3 id="choreography-or-orchestration">Choreography or orchestration?</h3>
<p>Both approaches are totally valid, and each has its pros and cons. We mentioned this topic when <a href="https://cloud.google.com/blog/topics/developers-practitioners/better-service-orchestration-workflows">introducing Workflows</a>. When should you choose one approach over the other? Choreography can be a better fit if services are not closely related, or if they can exist in different bounded contexts. Whereas orchestration might be best if you can describe the business logic of your application as a clear flow chart, which can then directly be described in a workflow definition. </p>
<h2 id="next-steps">Next steps</h2>
<p>To go further, we invite you to have a closer look at <a href="http://cloud.google.com/workflows">Workflows</a>, and its supported <a href="https://cloud.google.com/workflows#all-features">features</a>, by looking at the <a href="https://cloud.google.com/workflows/docs/overview">documentation</a>, particularly the <a href="https://cloud.google.com/workflows/docs/reference/syntax">reference documentation</a> and the <a href="https://cloud.google.com/workflows/docs/sample-workflows?hl=en">examples</a>. We also have a series of short articles that cover Workflows, with various <a href="https://glaforge.appspot.com/category/Google%20Cloud%20Platform">tips and tricks</a>, as well as introductions to Workflows, with a <a href="https://atamel.dev/posts/2020/09-08_first_look_at_workflows/">first look at Workflows</a> and some thoughts on <a href="https://glaforge.appspot.com/article/orchestrating-microservices-with-cloud-workflows">choreography vs orchestration</a>.</p>
<p>If you want to study a concrete use case, with an event-based architecture and an equivalent orchestrated approach, feel free to look into our <a href="https://g.co/codelabs/serverless-workshop">Serverless Workshop</a>. It offers codelabs spanning Cloud Functions, Cloud Run, App Engine, Eventarc, and Workflows. In particular, lab 6 is the one in which we converted the event-based model into an orchestration with Workflows. All the code is also available as <a href="https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop">open source on GitHub</a>.<br />
We look forward to hearing from you about your workflow experiments and needs. Feel free to reach out to us on Twitter at <a href="https://twitter.com/glaforge">@glaforge</a> and <a href="https://twitter.com/meteatamel">@meteatamel</a>.</p>
<p><a href="https://cloud.google.com/blog/products/application-development/get-to-know-google-cloud-workflows"></a></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Day 15 with Workflows — Built-in Cloud Logging function</title><link>https://glaforge.dev/posts/2021/02/10/day-15-with-workflows-built-in-cloud-logging-function/</link><pubDate>Wed, 10 Feb 2021 16:01:24 +0100</pubDate><guid>https://glaforge.dev/posts/2021/02/10/day-15-with-workflows-built-in-cloud-logging-function/</guid><description>&lt;p>In the two previous episodes, we saw how to
&lt;a href="https://glaforge.dev/posts/2021/02/03/day-14-with-workflows-subworkflows/">create and call subworkflows&lt;/a>,
and we applied this technique to making a reusable routine for logging with Cloud Logging.
However, there&amp;rsquo;s already a built-in function for that purpose! So let&amp;rsquo;s have a look at this integration.&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/V2hQekDwdRM?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video">&lt;/iframe>
&lt;/div>
&lt;p>To call the built-in logging function, just create a new step, and make a call to the &lt;code>sys.log&lt;/code> function:&lt;/p></description><content:encoded>
<![CDATA[<p>In the two previous episodes, we saw how to
<a href="https://glaforge.dev/posts/2021/02/03/day-14-with-workflows-subworkflows/">create and call subworkflows</a>,
and we applied this technique to making a reusable routine for logging with Cloud Logging.
However, there&rsquo;s already a built-in function for that purpose! So let&rsquo;s have a look at this integration.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/V2hQekDwdRM?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>To call the built-in logging function, just create a new step, and make a call to the <code>sys.log</code> function:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">logString</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>sys.log<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">text</span>:<span style="color:#bbb">  </span>Hello  Cloud  Logging!<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">severity</span>:<span style="color:#bbb">  </span>INFO<span style="color:#bbb">
</span></span></span></code></pre></div><p>This function takes a mandatory parameter: text. And an optional one: severity.</p>
<p>The text parameter accepts all types of supported values, so it&rsquo;s not only string,
but all kinds of numbers, as well as arrays and dictionaries.
Their string representation will be used as text.</p>
<p>The optional severity parameter is an enum that can take the values:
<code>DEFAULT</code>, <code>DEBUG</code>, <code>INFO</code>, <code>NOTICE</code>, <code>WARNING</code>, <code>ERROR</code>, <code>CRITICAL</code>, <code>ALERT</code>, <code>EMERGENCY</code>,
with <code>DEFAULT</code> being&hellip; the default value if you don&rsquo;t specify a severity!</p>
<p>Here&rsquo;s another example with a dictionary as parameter, which will be output as text in the logs, and a severity of <code>WARNING</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">createDict</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">person</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">name</span>:<span style="color:#bbb">  </span>Guillaume<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">kids</span>:<span style="color:#bbb">  </span><span style="color:#40a070">2</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">logDict</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>sys.log<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">text</span>:<span style="color:#bbb">  </span>${person}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">severity</span>:<span style="color:#bbb">  </span>WARNING<span style="color:#bbb">
</span></span></span></code></pre></div><p>Looking at the results in the cloud logging console, you will see both messages appear:</p>
<p><figure>
  <a href="#img-e6ccc852f25079deabb42826dbefd891">
    <img src="/img/workflows-days/w15-builtin-log.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-e6ccc852f25079deabb42826dbefd891">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-days/w15-builtin-log.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Don&rsquo;t hesitate to have a look at <a href="https://cloud.google.com/workflows/docs/reference/stdlib/sys/log">reference documentation</a>
to find more about the available built-in functions.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Day 14 with Workflows — Subworkflows</title><link>https://glaforge.dev/posts/2021/02/03/day-14-with-workflows-subworkflows/</link><pubDate>Wed, 03 Feb 2021 17:02:24 +0100</pubDate><guid>https://glaforge.dev/posts/2021/02/03/day-14-with-workflows-subworkflows/</guid><description>&lt;p>Workflows are made of sequences of steps and branches.
Sometimes, some particular sequence of steps can be repeated, and it would be a good idea to avoid error-prone repetitions in your workflow definition
(in particular if you change in one place, and forget to change in another place).
You can modularize your definition by creating subworkflows, a bit like subroutines or functions in programming languages.
For example, yesterday, we had a look at &lt;a href="https://glaforge.dev/posts/2021/02/02/day-13-with-workflows-logging-with-cloud-logging/">how to log to Cloud Logging&lt;/a>:
if you want to log in several places in your workflow, you can extract that routine in a subworkflow.&lt;/p></description><content:encoded>
<![CDATA[<p>Workflows are made of sequences of steps and branches.
Sometimes, some particular sequence of steps can be repeated, and it would be a good idea to avoid error-prone repetitions in your workflow definition
(in particular if you change in one place, and forget to change in another place).
You can modularize your definition by creating subworkflows, a bit like subroutines or functions in programming languages.
For example, yesterday, we had a look at <a href="https://glaforge.dev/posts/2021/02/02/day-13-with-workflows-logging-with-cloud-logging/">how to log to Cloud Logging</a>:
if you want to log in several places in your workflow, you can extract that routine in a subworkflow.</p>
<p>Let&rsquo;s see that in action in the video below, and you can read all the explanations afterwards:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/tbiFaO_LOdg?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>First things first, let&rsquo;s step back and look at the structure of workflow definitions.
You write a series of steps, directly in the main YAML file.
You can move back and forth between steps thanks to
<a href="https://glaforge.dev/posts/2020/12/04/day-4-with-workflows-jumping-with-switch-conditions/">jumps</a>,
but it wouldn&rsquo;t be convenient to use jumps to emulate subroutines
(remember the good old days of BASIC and its gotos?).
Instead, Cloud Workflows allows you to separate steps under a <code>main</code>, and subroutines under their own subroutine name.</p>
<p>So far we had just a sequence of steps:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">stepOne</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"> </span>...<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">stepTwo</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"> </span>...<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">stepThree</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"> </span>...<span style="color:#bbb">
</span></span></span></code></pre></div><p>Those steps are implicitly under a <code>main</code> routine.
And here&rsquo;s how to show this main routine explicitly, by having that <code>main</code> block, and <code>steps</code> underneath:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">main</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">stepOne</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"> </span>...<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">stepTwo</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"> </span>...<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">stepThree</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"> </span>...<span style="color:#bbb">
</span></span></span></code></pre></div><p>To create a subworkflow, we follow the same structure, but with a different name than <code>main</code>, but you can also pass parameters like so:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">subWorkflow</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">params:  [param1,  param2,  param3</span>:<span style="color:#bbb">  </span><span style="color:#4070a0">&#34;default value&#34;</span>]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">stepOne</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"> </span>...<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">stepTwo</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"> </span>...<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">stepThree</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"> </span>...<span style="color:#bbb">
</span></span></span></code></pre></div><p>Notice that you can pass several parameters, and that parameters can have default values when that parameter is not provided at the call site.</p>
<p>Then in your main flow, you can call that subworkflow with a <code>call</code> instruction.
Let&rsquo;s take a look at a concrete example, that simply concatenates two strings:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">main</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">greet</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>greet<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">greeting</span>:<span style="color:#bbb">  </span><span style="color:#4070a0">&#34;Hello&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">name</span>:<span style="color:#bbb">  </span><span style="color:#4070a0">&#34;Guillaume&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb">  </span>concatenation<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">returning</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span>${concatenation}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">greet</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">params:  [greeting,  name</span>:<span style="color:#bbb">  </span><span style="color:#4070a0">&#34;World&#34;</span>]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">append</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span>${greeting  +  &#34;, &#34;  +  name  +  &#34;!&#34;}<span style="color:#bbb">
</span></span></span></code></pre></div><p>In the <code>call</code> instruction, we pass the <code>greeting</code> and <code>name</code> arguments, and the result will contain the output of the subworkflow call.
In the subworkflow, we defined our parameters, and we have a single step just return an expression which is the desired greeting message concatenation.</p>
<p>One last example, but perhaps more useful than concatenating strings!
Let&rsquo;s turn yesterday&rsquo;s Cloud Logging integration into a reusable subworkflow.
That way, you&rsquo;ll be able to call the log subworkflow as many times as needed in your main workflow definition, without repeating yourself:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">main</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">first_log_msg</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>logMessage<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">msg</span>:<span style="color:#bbb">  </span><span style="color:#4070a0">&#34;First message&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">second_log_msg</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>logMessage<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">msg</span>:<span style="color:#bbb">  </span><span style="color:#4070a0">&#34;Second message&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">logMessage</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">params</span>:<span style="color:#bbb">  </span>[msg]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#062873;font-weight:bold">log</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>http.post<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb">  </span>https://logging.googleapis.com/v2/entries:write<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">auth</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb">  </span>OAuth2<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">body</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#062873;font-weight:bold">entries</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>- <span style="color:#062873;font-weight:bold">logName</span>:<span style="color:#bbb">  </span>${&#34;projects/&#34;  +  sys.get_env(&#34;GOOGLE_CLOUD_PROJECT_ID&#34;)  +  &#34;/logs/workflow_logger&#34;}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                      </span><span style="color:#062873;font-weight:bold">resource</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb">  </span><span style="color:#4070a0">&#34;audited_resource&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                        </span><span style="color:#062873;font-weight:bold">labels</span>:<span style="color:#bbb">  </span>{}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                      </span><span style="color:#062873;font-weight:bold">textPayload</span>:<span style="color:#bbb">  </span>${msg}<span style="color:#bbb">
</span></span></span></code></pre></div><p>And voila! We called our <code>logMessage</code> subworkflow twice in our main workflow, just passing the text message to log into Cloud Logging.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Day 13 with Workflows — Logging with Cloud Logging</title><link>https://glaforge.dev/posts/2021/02/02/day-13-with-workflows-logging-with-cloud-logging/</link><pubDate>Tue, 02 Feb 2021 17:35:19 +0100</pubDate><guid>https://glaforge.dev/posts/2021/02/02/day-13-with-workflows-logging-with-cloud-logging/</guid><description>&lt;p>Time to come back to our series on Cloud Workflows.
Sometimes, for debugging purposes or for auditing, it is useful to be able to log some information via Cloud Logging.
As we saw last month, you can
&lt;a href="https://glaforge.dev/posts/2020/12/15/day-8-with-workflows-calling-an-http-endpoint/">call HTTP endpoints&lt;/a> from your workflow.
We can actually use
&lt;a href="https://cloud.google.com/logging/docs/reference/v2/rest/v2/entries/write">Cloud Logging&amp;rsquo;s REST API&lt;/a> to log such messages!
Let&amp;rsquo;s see that in action.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>- &lt;span style="color:#062873;font-weight:bold">log&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">    &lt;/span>&lt;span style="color:#062873;font-weight:bold">call&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>http.post&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">    &lt;/span>&lt;span style="color:#062873;font-weight:bold">args&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">        &lt;/span>&lt;span style="color:#062873;font-weight:bold">url&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>https://logging.googleapis.com/v2/entries:write&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">        &lt;/span>&lt;span style="color:#062873;font-weight:bold">auth&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">            &lt;/span>&lt;span style="color:#062873;font-weight:bold">type&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>OAuth2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">        &lt;/span>&lt;span style="color:#062873;font-weight:bold">body&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">            &lt;/span>&lt;span style="color:#062873;font-weight:bold">entries&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">                &lt;/span>- &lt;span style="color:#062873;font-weight:bold">logName&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${&amp;#34;projects/&amp;#34; + sys.get_env(&amp;#34;GOOGLE_CLOUD_PROJECT_ID&amp;#34;) + &amp;#34;/logs/workflow_logger&amp;#34;}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">                  &lt;/span>&lt;span style="color:#062873;font-weight:bold">resource&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">                    &lt;/span>&lt;span style="color:#062873;font-weight:bold">type&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;audited_resource&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">                    &lt;/span>&lt;span style="color:#062873;font-weight:bold">labels&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>{}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">                  &lt;/span>&lt;span style="color:#062873;font-weight:bold">textPayload&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Hello World from Cloud Workflows!&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We call the &lt;a href="https://logging.googleapis.com/v2/entries:write">https://logging.googleapis.com/v2/entries:write&lt;/a> API endpoint to write new logging entries.
We authenticate via OAuth2&amp;mdash;as long as the service account used for the workflow execution allows it to use the logging API.
Then we pass a JSON structure as the body of the call, indicating the name of the logger to use,
which resources it applies to, and also the textPayload containing our text message. You could also use a ${} expression to log more complex values.&lt;/p></description><content:encoded>
<![CDATA[<p>Time to come back to our series on Cloud Workflows.
Sometimes, for debugging purposes or for auditing, it is useful to be able to log some information via Cloud Logging.
As we saw last month, you can
<a href="https://glaforge.dev/posts/2020/12/15/day-8-with-workflows-calling-an-http-endpoint/">call HTTP endpoints</a> from your workflow.
We can actually use
<a href="https://cloud.google.com/logging/docs/reference/v2/rest/v2/entries/write">Cloud Logging&rsquo;s REST API</a> to log such messages!
Let&rsquo;s see that in action.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">log</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>http.post<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb">  </span>https://logging.googleapis.com/v2/entries:write<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">auth</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb">  </span>OAuth2<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">body</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">entries</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>- <span style="color:#062873;font-weight:bold">logName</span>:<span style="color:#bbb">  </span>${&#34;projects/&#34;  +  sys.get_env(&#34;GOOGLE_CLOUD_PROJECT_ID&#34;)  +  &#34;/logs/workflow_logger&#34;}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                  </span><span style="color:#062873;font-weight:bold">resource</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb">  </span><span style="color:#4070a0">&#34;audited_resource&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span><span style="color:#062873;font-weight:bold">labels</span>:<span style="color:#bbb">  </span>{}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                  </span><span style="color:#062873;font-weight:bold">textPayload</span>:<span style="color:#bbb">  </span>Hello  World  from  Cloud  Workflows!<span style="color:#bbb">
</span></span></span></code></pre></div><p>We call the <a href="https://logging.googleapis.com/v2/entries:write">https://logging.googleapis.com/v2/entries:write</a> API endpoint to write new logging entries.
We authenticate via OAuth2&mdash;as long as the service account used for the workflow execution allows it to use the logging API.
Then we pass a JSON structure as the body of the call, indicating the name of the logger to use,
which resources it applies to, and also the textPayload containing our text message. You could also use a ${} expression to log more complex values.</p>
<p>Once this workflow definition is done and deployed, you can execute it, and you should see in the logs your message appear:</p>
<p><figure>
  <a href="#img-e02cfbf04d7403d351a529cb4bba4556">
    <img src="/img/workflows-days/w13-cloud-logging.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-e02cfbf04d7403d351a529cb4bba4556">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-days/w13-cloud-logging.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Voila! You can log messages to Cloud Logging!</p>
<p>Let&rsquo;s recap in this video:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/XwzSgBB6Kq4?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>In the next episode, we&rsquo;ll take advantage of subworkflows,
to create a reusable set of steps that you will be able to call several times throughout your workflow definition,
without repeating yourself, by turning this logging example into a subworkflow.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Day 12 with Workflows — Loops and iterations</title><link>https://glaforge.dev/posts/2020/12/23/day-12-with-workflows-loops-and-iterations/</link><pubDate>Wed, 23 Dec 2020 19:00:18 +0100</pubDate><guid>https://glaforge.dev/posts/2020/12/23/day-12-with-workflows-loops-and-iterations/</guid><description>&lt;p>In previous episodes of this Cloud Workflows series, we&amp;rsquo;ve learned about
&lt;a href="https://glaforge.dev/posts/2020/12/03/day-3-with-workflows-variable-assignment-and-expressions/">variable assignment&lt;/a>,
data structures like &lt;a href="https://glaforge.dev/posts/2020/12/09/day-6-with-workflows-arrays-and-dictionaries/">arrays&lt;/a>,
&lt;a href="https://glaforge.dev/posts/2020/12/02/day-2-with-workflows-a-workflow-is-made-of-steps-and-jumps/">jumps&lt;/a> and
&lt;a href="https://glaforge.dev/posts/2020/12/04/day-4-with-workflows-jumping-with-switch-conditions/">switch conditions&lt;/a> to move between steps,
and &lt;a href="https://glaforge.dev/posts/2020/12/03/day-3-with-workflows-variable-assignment-and-expressions/">expressions&lt;/a>
to do some computations, including potentially some built-in functions.&lt;/p>
&lt;p>With all these previous learnings, we are now equipped with all the tools to let us create loops and iterations,
like for example, iterating over the element of an array, perhaps to call an API several times but with different arguments.
So let&amp;rsquo;s see how to create such an iteration!&lt;/p></description><content:encoded>
<![CDATA[<p>In previous episodes of this Cloud Workflows series, we&rsquo;ve learned about
<a href="https://glaforge.dev/posts/2020/12/03/day-3-with-workflows-variable-assignment-and-expressions/">variable assignment</a>,
data structures like <a href="https://glaforge.dev/posts/2020/12/09/day-6-with-workflows-arrays-and-dictionaries/">arrays</a>,
<a href="https://glaforge.dev/posts/2020/12/02/day-2-with-workflows-a-workflow-is-made-of-steps-and-jumps/">jumps</a> and
<a href="https://glaforge.dev/posts/2020/12/04/day-4-with-workflows-jumping-with-switch-conditions/">switch conditions</a> to move between steps,
and <a href="https://glaforge.dev/posts/2020/12/03/day-3-with-workflows-variable-assignment-and-expressions/">expressions</a>
to do some computations, including potentially some built-in functions.</p>
<p>With all these previous learnings, we are now equipped with all the tools to let us create loops and iterations,
like for example, iterating over the element of an array, perhaps to call an API several times but with different arguments.
So let&rsquo;s see how to create such an iteration!</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/OXhV2cuKwo?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>First of all, let&rsquo;s prepare some variable assignments:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">define</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">array</span>:<span style="color:#bbb">  </span>[<span style="color:#4070a0">&#39;Google&#39;</span>,<span style="color:#bbb">  </span><span style="color:#4070a0">&#39;Cloud&#39;</span>,<span style="color:#bbb">  </span><span style="color:#4070a0">&#39;Workflows&#39;</span>]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb">  </span><span style="color:#4070a0">&#34;&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">i</span>:<span style="color:#bbb">  </span><span style="color:#40a070">0</span><span style="color:#bbb">
</span></span></span></code></pre></div><ul>
<li>The <code>array</code> variable will hold the values we&rsquo;ll be iterating over.</li>
<li>The <code>result</code> variable contains a string to which we&rsquo;ll append each values from the array.</li>
<li>And the <code>i</code> variable is an index, to know our position in the array.</li>
</ul>
<p>Next, like in a for loop of programming languages, we need to prepare a condition for the loop to finish. We&rsquo;ll do that in a dedicated step:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">checkCondition</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">switch</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">condition</span>:<span style="color:#bbb">  </span>${i  &lt;  len(array)}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">next</span>:<span style="color:#bbb">  </span>iterate<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">next</span>:<span style="color:#bbb">  </span>returnResult<span style="color:#bbb">
</span></span></span></code></pre></div><p>We define a <code>switch</code>, with a <code>condition</code> expression that compares the current index position with the length of the array, using the built-in <code>len()</code> function.
If the condition is true, we&rsquo;ll go to an <code>iterate</code> step. If it&rsquo;s false, we&rsquo;ll go to the ending step (called <code>returnResult</code> here).</p>
<p>Let&rsquo;s tackle the iteration body itself.
Here, it&rsquo;s quite simple, as we&rsquo;re just assigning new values to the variables:
we append the i-th element of the array into the result variable, and we increment the index by one.
Then we go back to the <code>checkCondition</code> step.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">iterate</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb">  </span>${result  +  array[i]  +  &#34; &#34;}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">i</span>:<span style="color:#bbb">  </span>${i+1}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">next</span>:<span style="color:#bbb">  </span>checkCondition<span style="color:#bbb">
</span></span></span></code></pre></div><p>Note that if we were doing something more convoluted, for example calling an HTTP endpoint with the element of the array as argument,
we would need two steps: one for the actual HTTP endpoint call, and one for incrementing the index value.
However in the example above, we&rsquo;re only assigning variables, so we did the whole body of the iteration in this simple assignment step.</p>
<p>When going through the <code>checkCondition</code> step, if the condition is not met (ie. we&rsquo;ve reached the end of the array), then we&rsquo;re redirected to the <code>returnResult</code> step:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">returnResult</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span>${result}<span style="color:#bbb">
</span></span></span></code></pre></div><p>This final step simply returns the value of the <code>result</code> variable.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Day 11 with Workflows — Sleeping in a workflow</title><link>https://glaforge.dev/posts/2020/12/22/day-11-with-workflows-sleeping-in-a-workflow/</link><pubDate>Tue, 22 Dec 2020 19:05:26 +0100</pubDate><guid>https://glaforge.dev/posts/2020/12/22/day-11-with-workflows-sleeping-in-a-workflow/</guid><description>&lt;p>Workflows are not necessarily instantaneous, and executions can span over a long period of time.
Some steps may potentially launch asynchronous operations, which might take seconds or minutes to finish, but you are not notified when the process is over.
So when you want for something to finish, for example before polling again to check the status of the async operation, you can introduce a sleep operation in your workflows.&lt;/p></description><content:encoded>
<![CDATA[<p>Workflows are not necessarily instantaneous, and executions can span over a long period of time.
Some steps may potentially launch asynchronous operations, which might take seconds or minutes to finish, but you are not notified when the process is over.
So when you want for something to finish, for example before polling again to check the status of the async operation, you can introduce a sleep operation in your workflows.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/uaW_Cv3RCxQ?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>To introduce a <a href="https://cloud.google.com/workflows/docs/reference/syntax">sleep operation</a>, add a step in the workflow with a call to the built-in sleep operation:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">someSleep</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>sys.sleep<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">seconds</span>:<span style="color:#bbb">  </span><span style="color:#40a070">10</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">returnOutput</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span>We  waited  for  10  seconds!<span style="color:#bbb">
</span></span></span></code></pre></div><p>A <code>sys.sleep</code> operation takes a <code>seconds</code> argument, where you can specify the number of seconds to wait.</p>
<p>By combining conditional jumps and sleep operations, you can easily implement polling some resource or API at a regular interval, to double check that it completed.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Day 10 with Workflows — Accessing built-in environment variables</title><link>https://glaforge.dev/posts/2020/12/17/day-10-with-workflows-accessing-built-in-environment-variables/</link><pubDate>Thu, 17 Dec 2020 19:13:07 +0100</pubDate><guid>https://glaforge.dev/posts/2020/12/17/day-10-with-workflows-accessing-built-in-environment-variables/</guid><description>&lt;p>&lt;a href="https://cloud.google.com/workflows">Google Cloud Workflows&lt;/a> offers a few built-in environment variables that are accessible from your workflow executions.&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/sJQWbo49pWg?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video">&lt;/iframe>
&lt;/div>
&lt;p>There are currently &lt;a href="https://cloud.google.com/workflows/docs/reference/environment-variables">5 environment variables&lt;/a> that are defined:&lt;/p>
&lt;ul>
&lt;li>&lt;code>GOOGLE_CLOUD_PROJECT_NUMBER&lt;/code>: The workflow project&amp;rsquo;s number.&lt;/li>
&lt;li>&lt;code>GOOGLE_CLOUD_PROJECT_ID&lt;/code>: The workflow project&amp;rsquo;s identifier.&lt;/li>
&lt;li>&lt;code>GOOGLE_CLOUD_LOCATION&lt;/code>: The workflow&amp;rsquo;s location.&lt;/li>
&lt;li>&lt;code>GOOGLE_CLOUD_WORKFLOW_ID&lt;/code>: The workflow&amp;rsquo;s identifier.&lt;/li>
&lt;li>&lt;code>GOOGLE_CLOUD_WORKFLOW_REVISION_ID&lt;/code>: The workflow&amp;rsquo;s revision identifier.&lt;/li>
&lt;/ul>
&lt;p>Let&amp;rsquo;s see how to access them from our workflow definition:&lt;/p></description><content:encoded>
<![CDATA[<p><a href="https://cloud.google.com/workflows">Google Cloud Workflows</a> offers a few built-in environment variables that are accessible from your workflow executions.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/sJQWbo49pWg?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>There are currently <a href="https://cloud.google.com/workflows/docs/reference/environment-variables">5 environment variables</a> that are defined:</p>
<ul>
<li><code>GOOGLE_CLOUD_PROJECT_NUMBER</code>: The workflow project&rsquo;s number.</li>
<li><code>GOOGLE_CLOUD_PROJECT_ID</code>: The workflow project&rsquo;s identifier.</li>
<li><code>GOOGLE_CLOUD_LOCATION</code>: The workflow&rsquo;s location.</li>
<li><code>GOOGLE_CLOUD_WORKFLOW_ID</code>: The workflow&rsquo;s identifier.</li>
<li><code>GOOGLE_CLOUD_WORKFLOW_REVISION_ID</code>: The workflow&rsquo;s revision identifier.</li>
</ul>
<p>Let&rsquo;s see how to access them from our workflow definition:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">envVars</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">projectID</span>:<span style="color:#bbb">  </span>${sys.get_env(&#34;GOOGLE_CLOUD_PROJECT_ID&#34;)}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">projectNum</span>:<span style="color:#bbb">  </span>${sys.get_env(&#34;GOOGLE_CLOUD_PROJECT_NUMBER&#34;)}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">projectLocation</span>:<span style="color:#bbb">  </span>${sys.get_env(&#34;GOOGLE_CLOUD_LOCATION&#34;)}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">workflowID</span>:<span style="color:#bbb">  </span>${sys.get_env(&#34;GOOGLE_CLOUD_WORKFLOW_ID&#34;)}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">workflowRev</span>:<span style="color:#bbb">  </span>${sys.get_env(&#34;GOOGLE_CLOUD_WORKFLOW_REVISION_ID&#34;)}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">output</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span>${projectID  +  &#34; &#34;  +  projectNum  +  &#34; &#34;  +  projectLocation  +  &#34; &#34;  +  workflowID  +  &#34; &#34;  +  workflowRev}<span style="color:#bbb">
</span></span></span></code></pre></div><p>We use the built-in <code>sys.get_env()</code> function to access those variables.
We&rsquo;ll revisit the various existing built-in functions in later episodes.</p>
<p>Then when you execute this workflow, you&rsquo;ll get an output like this:</p>
<pre tabindex="0"><code>&#34;workflows-days 783331365595 europe-west4 w10-builtin-env-vars 000001-3af&#34;
</code></pre><p>There&rsquo;s one variable I&rsquo;d like to see added to this list, that would be the current execution ID.
That could potentially be useful for identifying a particular execution, when looking in the logs, to reason about potential failure, or for auditing purposes.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Day 9 with Workflows — Deploying and executing Workflows from the command-line</title><link>https://glaforge.dev/posts/2020/12/16/day-9-with-workflows-deploying-and-executing-workflows-from-the-command-line/</link><pubDate>Wed, 16 Dec 2020 19:44:08 +0100</pubDate><guid>https://glaforge.dev/posts/2020/12/16/day-9-with-workflows-deploying-and-executing-workflows-from-the-command-line/</guid><description>&lt;p>So far, in this series on &lt;a href="https://cloud.google.com/workflows">Cloud Workflows&lt;/a>,
we&amp;rsquo;ve only used the Google Cloud Console UI to manage our workflow definitions, and their executions.
But it&amp;rsquo;s also possible to deploy new definitions and update existing ones from the command-line,
using the &lt;a href="https://cloud.google.com/sdk/">GCloud SDK&lt;/a>. Let&amp;rsquo;s see how to do that!&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/rVTPRUuidPI?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video">&lt;/iframe>
&lt;/div>
&lt;p>If you don&amp;rsquo;t already have an existing service account, you should create one following these
&lt;a href="https://cloud.google.com/workflows/docs/creating-updating-workflow#gcloud">instructions&lt;/a>.
I&amp;rsquo;m going to use the workflow-sa service account I created for the purpose of this demonstration.&lt;/p></description><content:encoded>
<![CDATA[<p>So far, in this series on <a href="https://cloud.google.com/workflows">Cloud Workflows</a>,
we&rsquo;ve only used the Google Cloud Console UI to manage our workflow definitions, and their executions.
But it&rsquo;s also possible to deploy new definitions and update existing ones from the command-line,
using the <a href="https://cloud.google.com/sdk/">GCloud SDK</a>. Let&rsquo;s see how to do that!</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/rVTPRUuidPI?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>If you don&rsquo;t already have an existing service account, you should create one following these
<a href="https://cloud.google.com/workflows/docs/creating-updating-workflow#gcloud">instructions</a>.
I&rsquo;m going to use the workflow-sa service account I created for the purpose of this demonstration.</p>
<p>Our workflow definition is a simple &ldquo;hello world&rdquo; like the one we created for
<a href="https://glaforge.dev/posts/2020/12/01/day-1-with-workflows-your-first-step-to-hello-world/">day #1</a>
of our exploration of Google Cloud Workflows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">hello</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span>Hello  from  gcloud!<span style="color:#bbb">
</span></span></span></code></pre></div><p>To deploy this workflow definition, we&rsquo;ll launch the following gcloud command,
specifying the name of our workflow, passing the local source definition, and the service account:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ gcloud beta workflows deploy w09-new-workflow-from-cli <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --source<span style="color:#666">=</span>w09-hello-from-gcloud.yaml <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --service-account<span style="color:#666">=</span>workflow-sa@workflows-days.iam.gserviceaccount.com
</span></span></code></pre></div><p>You can also add labels with the <code>--labels</code> flag, and a description with the <code>--description</code> flag, just like in the Google Cloud Console UI.</p>
<p>If you want to update the workflow definition, this is also the same command to invoke, passing the new version of your definition file.</p>
<p>Time to create an execution of our workflow!</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ gcloud beta workflows run w09-new-workflow-from-cli
</span></span></code></pre></div><p>You will see an output similar to this:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>Waiting for execution [d4a3f4d4-db45-48dc-9c02-d25a05b0e0ed] to complete...done.<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">argument</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#39;null&#39;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">endTime</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#39;2020-12-16T11:32:25.663937037Z&#39;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">name</span>:<span style="color:#bbb"> </span>projects/783331365595/locations/us-central1/workflows/w09-new-workflow-from-cli/executions/d4a3f4d4-db45-48dc-9c02-d25a05b0e0ed<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#39;&#34;Hello from gcloud!&#34;&#39;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">startTime</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#39;2020-12-16T11:32:25.526194298Z&#39;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">state</span>:<span style="color:#bbb"> </span>SUCCEEDED<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">workflowRevisionId</span>:<span style="color:#bbb"> </span><span style="color:#40a070">000001</span>-47f<span style="color:#bbb">
</span></span></span></code></pre></div><p>Our workflow being very simple, it executed and completed right away, hence why you see the result string
(our Hello from gcloud! message), as well as the state as SUCCEEDED.
However, workflows often take longer to execute, consisting of many steps.
If the workflow hasn&rsquo;t yet completed, you&rsquo;ll see its status as <code>ACTIVE</code> instead, or potentially <code>FAILED</code> if something went wrong.</p>
<p>When the workflow takes a long time to complete, you can check the status of the last execution from your shell session with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ gcloud beta workflows executions describe-last
</span></span></code></pre></div><p>If you want to know about the ongoing workflow executions:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ gcloud beta workflows executions list your-workflow-name
</span></span></code></pre></div><p>It&rsquo;ll give you a list of operation IDs for those ongoing executions. You can then inspect a particular one with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ gcloud beta workflows executions describe the-operation-id
</span></span></code></pre></div><p>There are other operations on executions, to wait for an execution to finish, or even cancel an ongoing, unfinished execution.</p>
<p>You can learn more about workflow execution in the <a href="https://cloud.google.com/workflows/docs/executing-workflow">documentation</a>.
And in some upcoming episodes, we&rsquo;ll also have a look at how to create workflow executions from client libraries, and from the Cloud Workflows REST API.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Day 8 with Workflows — Calling an HTTP endpoint</title><link>https://glaforge.dev/posts/2020/12/15/day-8-with-workflows-calling-an-http-endpoint/</link><pubDate>Tue, 15 Dec 2020 19:51:12 +0100</pubDate><guid>https://glaforge.dev/posts/2020/12/15/day-8-with-workflows-calling-an-http-endpoint/</guid><description>&lt;p>Time to do something pretty handy: calling an HTTP endpoint, from your Google Cloud Workflows definitions.
Whether calling GCP specific APIs such as the ML APIs, REST APIs of other products like Cloud Firestore,
or when calling your own services, third-party, external APIs, this capability lets you plug your business processes to the external world!&lt;/p>
&lt;p>Let&amp;rsquo;s see calling HTTP endpoints in action in the following video, before diving into the details below:&lt;/p></description><content:encoded>
<![CDATA[<p>Time to do something pretty handy: calling an HTTP endpoint, from your Google Cloud Workflows definitions.
Whether calling GCP specific APIs such as the ML APIs, REST APIs of other products like Cloud Firestore,
or when calling your own services, third-party, external APIs, this capability lets you plug your business processes to the external world!</p>
<p>Let&rsquo;s see calling HTTP endpoints in action in the following video, before diving into the details below:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/jyIonG-u4eM?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>By default, when creating a new workflow definition, a default snippet / example is provided for your inspiration.
We&rsquo;ll take a look at it for this article.
There are actually two HTTP endpoint calls, the latter depending on the former:
the first step (<code>getCurrentTime</code>) is a cloud function returning the day of the week,
whereas the second step (<code>readWikipedia</code>) searches Wikipedia for articles about that day of the week.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">getCurrentTime</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>http.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb">  </span>https://us-central1-workflowsample.cloudfunctions.net/datetime<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb">  </span>CurrentDateTime<span style="color:#bbb">
</span></span></span></code></pre></div><p>The <code>getCurrentTime</code> step contains a call attribute of type <code>http.get</code>, to make HTTP GET requests to an API endpoint.
You have the ability to do either <code>call: http.get</code> or <code>call: http.post</code>.
For other methods, you&rsquo;ll have to do call: <code>http.request</code>, and add another key/value pair under <code>args</code>, with method:
<code>GET</code>, <code>POST</code>, <code>PATCH</code> or <code>DELETE</code>.
Under <code>args</code>, for now, we&rsquo;ll just put the URL of our HTTP endpoint.
The last key will be the result, which gives the name of a new variable that will contain the response of our HTTP request.</p>
<p>Let&rsquo;s call Wikipedia with our day of the week search query:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">readWikipedia</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>http.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb">  </span>https://en.wikipedia.org/w/api.php<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">query</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">action</span>:<span style="color:#bbb">  </span>opensearch<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">search</span>:<span style="color:#bbb">  </span>${CurrentDateTime.body.dayOfTheWeek}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb">  </span>WikiResult<span style="color:#bbb">
</span></span></span></code></pre></div><p>Same thing with <code>call</code>, and <code>args.url</code>, however, we have a query where you can define the query parameters for the Wikipedia API.
Also note how we can pass data from the previous step function invocation: <code>CurrentDateTime.body.dayOfTheWeek</code>.
We retrieve the body of the response of the previous call, and from there, we get the <code>dayOfTheWeek</code> key in the resulting JSON document.
We then return <code>WikiResult</code>, which is the response of that new API endpoint call.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">returnOutput</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span>${WikiResult.body[1]}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Then, the last step is here to return the result of our search. We retrieve the body of the response.
The response&rsquo;s body is an array, with a first term being the search query,
and the second item is the following array of document names, which is what our workflow execution will return:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>[
</span></span><span style="display:flex;"><span> <span style="color:#4070a0">&#34;Monday&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#4070a0">&#34;Monday Night Football&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#4070a0">&#34;Monday Night Wars&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#4070a0">&#34;Monday Night Countdown&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#4070a0">&#34;Monday Morning (newsletter)&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#4070a0">&#34;Monday Night Golf&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#4070a0">&#34;Monday Mornings&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#4070a0">&#34;Monday (The X-Files)&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#4070a0">&#34;Monday&#39;s Child&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#4070a0">&#34;Monday.com&#34;</span>
</span></span><span style="display:flex;"><span>]
</span></span></code></pre></div><p>So our whole workflow was able to orchestrate two independent API endpoints, one after the other.
Instead of having two APIs that are coupled via some messaging passing mechanism,
or worse, via explicit calls to one or the other, Cloud Workflows is here to organize those two calls.
It&rsquo;s the orchestration approach, instead of a choreography of services
(see my previous article on <a href="https://glaforge.dev/posts/2020/11/18/orchestrating-microservices-with-cloud-workflows/">orchestration vs choreography</a>,
and my colleague&rsquo;s article on <a href="https://cloud.google.com/blog/topics/developers-practitioners/better-service-orchestration-workflows">better service orchestration</a> with Cloud Workflows).</p>
<p>To come back to the details of API endpoint calls, here&rsquo;s their structure:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">STEP_NAME</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb">  </span>{http.get|http.post|http.request}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb">  </span>URL_VALUE<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">[method</span>:<span style="color:#bbb">  </span>REQUEST_METHOD]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">[headers</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>KEY:VALUE  ...]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">[body</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>KEY:VALUE  ...]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">[query</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>KEY:VALUE  ...]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">[auth</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>type:{OIDC|OAuth2}]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">[timeout</span>:<span style="color:#bbb">  </span>VALUE_IN_SECONDS]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">[result</span>:<span style="color:#bbb">  </span>RESPONSE_VALUE]<span style="color:#bbb">
</span></span></span></code></pre></div><p>In addition to the URL, the method and query, note that you can pass headers and a body. There is also a built-in mechanism for authentication which works with GCP APIs: the authentication is done transparently. You can also specify a timeout in seconds, if you want to fail fast and not wait forever a response that never comes. But we&rsquo;ll come back to error handling in some of our upcoming articles.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Day 7 with Workflows — Pass an input argument to your workflow</title><link>https://glaforge.dev/posts/2020/12/10/day-7-with-workflows-pass-an-input-argument-to-your-workflow/</link><pubDate>Thu, 10 Dec 2020 20:04:09 +0100</pubDate><guid>https://glaforge.dev/posts/2020/12/10/day-7-with-workflows-pass-an-input-argument-to-your-workflow/</guid><description>&lt;p>All the workflow definitions we&amp;rsquo;ve seen so far, in this series, were self-contained.
They were not parameterized. But we often need our business processes to take arguments
(the ID of an order, the details of the order, etc.), so that we can treat those input values and do something about them.
That&amp;rsquo;s where workflow input parameters become useful!&lt;/p>
&lt;p>Let&amp;rsquo;s start with a simple greeting message that we want to customize with a &lt;code>firstname&lt;/code> and &lt;code>lastname&lt;/code>.
We&amp;rsquo;d like our workflow to look something like this:&lt;/p></description><content:encoded>
<![CDATA[<p>All the workflow definitions we&rsquo;ve seen so far, in this series, were self-contained.
They were not parameterized. But we often need our business processes to take arguments
(the ID of an order, the details of the order, etc.), so that we can treat those input values and do something about them.
That&rsquo;s where workflow input parameters become useful!</p>
<p>Let&rsquo;s start with a simple greeting message that we want to customize with a <code>firstname</code> and <code>lastname</code>.
We&rsquo;d like our workflow to look something like this:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">output</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span>${&#34;Your  name  is  &#34; + person.firstname + &#34;  &#34; + person.lastname}<span style="color:#bbb">
</span></span></span></code></pre></div><p>In the example above, we have a <code>person</code> variable, on which we&rsquo;re requesting the fields <code>firstname</code> and <code>lastname</code>.
This is actually a dictionary. But how do we let Cloud Workflows know about this variable? We need to define it somehow.</p>
<p>Workflow arguments are global to all the steps, so they need to be defined outside the scope of the steps themselves.
Actually, workflows can be structured in sub-workflows:
there&rsquo;s a main workflow, and possibly additional sub-workflows which are like routines or internal function definitions.
We&rsquo;ll revisit the topic of sub-workflows in a later article.
To declare our input parameter, we&rsquo;ll do it at the level of the main workflow, but in a more explicit fashion, with the following notation:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">main</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">params</span>:<span style="color:#bbb">  </span>[person]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">output</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span>${&#34;Your  name  is  &#34; + person.firstname + &#34;  &#34; + person.lastname}<span style="color:#bbb">
</span></span></span></code></pre></div><p>We explicitly show the name of our main workflow. We use the <code>params</code> instruction.
Note that our single argument, <code>person</code>, is surrounded by square brackets.
The main workflow can only take a single dictionary parameter, however, as we&rsquo;ll see later,
sub-workflows can take several input arguments, hence the square brackets notation to specify a list of arguments.</p>
<p><figure>
  <a href="#img-f2940eb12fa1533e66de4bd3b599bd08">
    <img src="/img/workflows-days/w7-input-argument.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-f2940eb12fa1533e66de4bd3b599bd08">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-days/w7-input-argument.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>How do we pass this input argument?
In the execution screen, in the input pane on the left, we create a JSON object, with a firstname and lastname keys.
This JSON object is the dictionary in the person variable of our workflow definition.</p>
<p>In this video, you&rsquo;ll see input arguments in action:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/3dyKx2zBiXA?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Day 6 with Workflows — Arrays and dictionaries</title><link>https://glaforge.dev/posts/2020/12/09/day-6-with-workflows-arrays-and-dictionaries/</link><pubDate>Wed, 09 Dec 2020 22:14:55 +0100</pubDate><guid>https://glaforge.dev/posts/2020/12/09/day-6-with-workflows-arrays-and-dictionaries/</guid><description>&lt;p>So far, in this series of articles on &lt;a href="https://cloud.google.com/workflows">Cloud Workflows&lt;/a>,
we have used simple data types, like strings, numbers and boolean values.
However, it&amp;rsquo;s possible to use more complex data structures,
like &lt;a href="https://cloud.google.com/workflows/docs/reference/syntax?hl=en#arrays">arrays&lt;/a> and
&lt;a href="https://cloud.google.com/workflows/docs/reference/syntax?hl=en#dictionaries">dictionaries&lt;/a>.
In this new episode, we&amp;rsquo;re going to use those new structures.&lt;/p>
&lt;p>Arrays can be defined inline (like &lt;code>anArray&lt;/code>) or spanning over several lines (like &lt;code>anotherArray&lt;/code>):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>- &lt;span style="color:#062873;font-weight:bold">assignment&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">    &lt;/span>&lt;span style="color:#062873;font-weight:bold">assign&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">        &lt;/span>- &lt;span style="color:#062873;font-weight:bold">anArray&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>[&lt;span style="color:#4070a0">&amp;#34;a&amp;#34;&lt;/span>,&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;b&amp;#34;&lt;/span>,&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;c&amp;#34;&lt;/span>]&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">        &lt;/span>- &lt;span style="color:#062873;font-weight:bold">anotherArray&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">            &lt;/span>- &lt;span style="color:#bbb"> &lt;/span>one&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">            &lt;/span>- &lt;span style="color:#bbb"> &lt;/span>two&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>- &lt;span style="color:#062873;font-weight:bold">output&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">    &lt;/span>&lt;span style="color:#062873;font-weight:bold">return&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${anArray[0] + anotherArray[1]}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The output step will return the string &lt;code>&amp;quot;atwo&amp;quot;&lt;/code>.&lt;/p></description><content:encoded>
<![CDATA[<p>So far, in this series of articles on <a href="https://cloud.google.com/workflows">Cloud Workflows</a>,
we have used simple data types, like strings, numbers and boolean values.
However, it&rsquo;s possible to use more complex data structures,
like <a href="https://cloud.google.com/workflows/docs/reference/syntax?hl=en#arrays">arrays</a> and
<a href="https://cloud.google.com/workflows/docs/reference/syntax?hl=en#dictionaries">dictionaries</a>.
In this new episode, we&rsquo;re going to use those new structures.</p>
<p>Arrays can be defined inline (like <code>anArray</code>) or spanning over several lines (like <code>anotherArray</code>):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">assignment</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">anArray</span>:<span style="color:#bbb">  </span>[<span style="color:#4070a0">&#34;a&#34;</span>,<span style="color:#bbb">  </span><span style="color:#4070a0">&#34;b&#34;</span>,<span style="color:#bbb">  </span><span style="color:#4070a0">&#34;c&#34;</span>]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">anotherArray</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>- <span style="color:#bbb"> </span>one<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>- <span style="color:#bbb"> </span>two<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">output</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span>${anArray[0] + anotherArray[1]}<span style="color:#bbb">
</span></span></span></code></pre></div><p>The output step will return the string <code>&quot;atwo&quot;</code>.</p>
<p>For dictionaries, you can define them as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">assignment</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">person</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">firstname</span>:<span style="color:#bbb">  </span><span style="color:#4070a0">&#34;Guillaume&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">lastname</span>:<span style="color:#bbb">  </span><span style="color:#4070a0">&#34;Laforge&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">age</span>:<span style="color:#bbb">  </span><span style="color:#40a070">43</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">kids</span>:<span style="color:#bbb">  </span>[<span style="color:#4070a0">&#34;Marion&#34;</span>,<span style="color:#bbb">  </span><span style="color:#4070a0">&#34;Erine&#34;</span>]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">output</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span>${person.firstname  +  &#34; and &#34;  + person.kids[1]}<span style="color:#bbb">
</span></span></span></code></pre></div><p>The output step will return the string <code>&quot;Guillaume and Erine&quot;</code>.</p>
<p>Notice that we nested an array within a dictionary.
So you can easily create dictionaries containing arrays, containing other dictionaries, etc,
just like any JSON or YAML structures.</p>
<p>In the example we were able to access the second kid of the person, mixing both the field (dot)
and index (square brackets) notations to access fields of our dictionary, and elements of our array.</p>
<p>This video shows both arrays and dictionaries in action:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/9JrqlV5s11Q?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>In the coming articles, we&rsquo;ll see that such data structures are handy for dealing with API endpoint calls.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Day 5 with Workflows — Visualizing the structure of your workflow definition</title><link>https://glaforge.dev/posts/2020/12/08/day-5-with-workflows-visualizing-the-structure-of-your-workflow-definition/</link><pubDate>Tue, 08 Dec 2020 22:26:13 +0100</pubDate><guid>https://glaforge.dev/posts/2020/12/08/day-5-with-workflows-visualizing-the-structure-of-your-workflow-definition/</guid><description>&lt;p>So far, in our Cloud Workflows series, we have seen some of the YAML syntax for defining workflows.
However, steps are defined after each other, as a series of step definitions,
but in spite of the jump instructions, the conditionals,
you don&amp;rsquo;t really see visually what is going to be the next potential step in a workflow execution.&lt;/p>
&lt;p>Fortunately, a new UI enhancement has landed in the Google Cloud Console:
the ability to visualize a workflow definition with a graph, when you&amp;rsquo;re editing the definition.
Furthermore, the graph is updated in quasi real-time as you make updates to the definition.&lt;/p></description><content:encoded>
<![CDATA[<p>So far, in our Cloud Workflows series, we have seen some of the YAML syntax for defining workflows.
However, steps are defined after each other, as a series of step definitions,
but in spite of the jump instructions, the conditionals,
you don&rsquo;t really see visually what is going to be the next potential step in a workflow execution.</p>
<p>Fortunately, a new UI enhancement has landed in the Google Cloud Console:
the ability to visualize a workflow definition with a graph, when you&rsquo;re editing the definition.
Furthermore, the graph is updated in quasi real-time as you make updates to the definition.</p>
<p><figure>
  <a href="#img-cca66c989c43043be376be5d0d856289">
    <img src="/img/workflows-days/w05-graph.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-cca66c989c43043be376be5d0d856289">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-days/w05-graph.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Let&rsquo;s see this in action in the video below:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/RQ11ATLxf3I?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>Thanks to this visualization, it&rsquo;s easier to further understand how your workflow definition is structured, how executions operate. You can more easily track which steps follows a particular step.</p>
<p>Enjoy!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Day 4 with Workflows — Jumping with switch conditions</title><link>https://glaforge.dev/posts/2020/12/04/day-4-with-workflows-jumping-with-switch-conditions/</link><pubDate>Fri, 04 Dec 2020 22:50:12 +0100</pubDate><guid>https://glaforge.dev/posts/2020/12/04/day-4-with-workflows-jumping-with-switch-conditions/</guid><description>&lt;p>In the previous articles about Google &lt;a href="https://cloud.google.com/workflows">Cloud Workflows&lt;/a>,
we talked about how to &lt;a href="https://glaforge.dev/posts/2020/12/03/day-3-with-workflows-variable-assignment-and-expressions/">assign variables, create expressions&lt;/a>,
and also how to &lt;a href="https://glaforge.dev/posts/2020/12/02/day-2-with-workflows-a-workflow-is-made-of-steps-and-jumps/">jump from a step to another&lt;/a>.
It&amp;rsquo;s time to combine both aspects to understand how we can do conditional jumps, thanks to the &lt;code>switch&lt;/code> instruction.&lt;/p>
&lt;p>Let&amp;rsquo;s start with a first step defining a variable, whose value we&amp;rsquo;ll use in our switch condition:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>- &lt;span style="color:#062873;font-weight:bold">assignement&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">    &lt;/span>&lt;span style="color:#062873;font-weight:bold">assign&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">        &lt;/span>- &lt;span style="color:#062873;font-weight:bold">number&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">42&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then we&amp;rsquo;re going to create our second step that will use a &lt;code>switch&lt;/code> instruction, with an expression:&lt;/p></description><content:encoded>
<![CDATA[<p>In the previous articles about Google <a href="https://cloud.google.com/workflows">Cloud Workflows</a>,
we talked about how to <a href="https://glaforge.dev/posts/2020/12/03/day-3-with-workflows-variable-assignment-and-expressions/">assign variables, create expressions</a>,
and also how to <a href="https://glaforge.dev/posts/2020/12/02/day-2-with-workflows-a-workflow-is-made-of-steps-and-jumps/">jump from a step to another</a>.
It&rsquo;s time to combine both aspects to understand how we can do conditional jumps, thanks to the <code>switch</code> instruction.</p>
<p>Let&rsquo;s start with a first step defining a variable, whose value we&rsquo;ll use in our switch condition:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">assignement</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">number</span>:<span style="color:#bbb">  </span><span style="color:#40a070">42</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>Then we&rsquo;re going to create our second step that will use a <code>switch</code> instruction, with an expression:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">evaluate</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">switch</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">condition</span>:<span style="color:#bbb">  </span>${number  &gt; 100}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">next</span>:<span style="color:#bbb">  </span>highValue<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">condition</span>:<span style="color:#bbb">  </span>${number  &lt;  100}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">next</span>:<span style="color:#bbb">  </span>lowValue<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">next</span>:<span style="color:#bbb">  </span>end<span style="color:#bbb">
</span></span></span></code></pre></div><p>We define two conditions with two expressions, checking if the number is above or below 100,
then we go to a different step (<code>highValue</code> or <code>lowValue</code> steps).
If none of the conditions are met, we go to the end of the workflow
(or we could return some value or raise some error).</p>
<p>We also need our two steps to go to:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">highValue</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span><span style="color:#4070a0">&#34;It&#39;s high!&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">lowValue</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span><span style="color:#4070a0">&#34;It&#39;s rather low!&#34;</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>And we&rsquo;re done! If the number is <code>42</code>, like in our case,
the execution of the workflow will go through the lowValue step,
and return the string saying that it&rsquo;s a low value.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/thSKszcLWSg?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>Thanks to <a href="https://cloud.google.com/workflows/docs/reference/syntax?hl=en#jumps">switch conditionals</a>,
with expressions and jumps, we can have non-linear logic in our workflow definitions.
In upcoming articles, we will also have a look at how to use more complex data structures
like arrays and dictionaries, and how to define inputs and outputs.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Day 3 with Workflows — Variable assignment and expressions</title><link>https://glaforge.dev/posts/2020/12/03/day-3-with-workflows-variable-assignment-and-expressions/</link><pubDate>Thu, 03 Dec 2020 23:00:14 +0100</pubDate><guid>https://glaforge.dev/posts/2020/12/03/day-3-with-workflows-variable-assignment-and-expressions/</guid><description>&lt;p>Now that we have multiple steps in our workflow definition,
let&amp;rsquo;s see how we can pass data around, from a step to another.&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-171bae7a2cbbbadea9bc66cb278ca766">
&lt;img src="https://glaforge.dev/img/workflows-days/w03-var-assign.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-171bae7a2cbbbadea9bc66cb278ca766">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/workflows-days/w03-var-assign.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>In a step, you can assign values to variables.
Those values can be ints, doubles, strings, or booleans (and also null).
Use the &lt;code>assign&lt;/code> keyword as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>- &lt;span style="color:#062873;font-weight:bold">assignments&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">    &lt;/span>&lt;span style="color:#062873;font-weight:bold">assign&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">        &lt;/span>- &lt;span style="color:#062873;font-weight:bold">two&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">2&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">        &lt;/span>- &lt;span style="color:#062873;font-weight:bold">pi&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">3.14&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">        &lt;/span>- &lt;span style="color:#062873;font-weight:bold">message&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;Hello&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">        &lt;/span>- &lt;span style="color:#062873;font-weight:bold">bool&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">True&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Those variables are available in the whole scope of the workflow, and can be accessed in other steps.
So let&amp;rsquo;s see how we can do something with those variables.
Let&amp;rsquo;s add a second step to our workflow definition:&lt;/p></description><content:encoded>
<![CDATA[<p>Now that we have multiple steps in our workflow definition,
let&rsquo;s see how we can pass data around, from a step to another.</p>
<p><figure>
  <a href="#img-171bae7a2cbbbadea9bc66cb278ca766">
    <img src="/img/workflows-days/w03-var-assign.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-171bae7a2cbbbadea9bc66cb278ca766">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-days/w03-var-assign.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>In a step, you can assign values to variables.
Those values can be ints, doubles, strings, or booleans (and also null).
Use the <code>assign</code> keyword as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">assignments</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">two</span>:<span style="color:#bbb">  </span><span style="color:#40a070">2</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">pi</span>:<span style="color:#bbb">  </span><span style="color:#40a070">3.14</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">message</span>:<span style="color:#bbb">  </span><span style="color:#4070a0">&#34;Hello&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">bool</span>:<span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">True</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>Those variables are available in the whole scope of the workflow, and can be accessed in other steps.
So let&rsquo;s see how we can do something with those variables.
Let&rsquo;s add a second step to our workflow definition:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">twoPi</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb">  </span>${&#34;Twice  pi  is  &#34; + string(two * pi)}<span style="color:#bbb">
</span></span></span></code></pre></div><p>We are using the <code>${}</code> notation to create an expression.
We&rsquo;re multiplying two numbers, we&rsquo;re converting them to a string,
and we&rsquo;re concatenating two strings together, to get our final value.</p>
<p>Note that not all operations are allowed on all types, so you might need to do some conversions
with built-in <a href="https://cloud.google.com/workflows/docs/reference/syntax?hl=en#conversion_functions">conversion functions</a> like the <code>string()</code> function in our example.
There are all sorts of arithmetic operators or boolean logic operators.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/dygoGp_tcCk?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>For more information, you can read about
<a href="https://cloud.google.com/workflows/docs/reference/syntax?hl=en#assign-step">variable assignments</a>,
<a href="https://cloud.google.com/workflows/docs/reference/syntax?hl=en#data_types">data types</a>, and
<a href="https://cloud.google.com/workflows/docs/reference/syntax?hl=en#expressions">expressions</a>.
Next time, we&rsquo;ll also have a look at more complex data types.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Day 2 with Workflows — A workflow is made of steps and jumps</title><link>https://glaforge.dev/posts/2020/12/02/day-2-with-workflows-a-workflow-is-made-of-steps-and-jumps/</link><pubDate>Wed, 02 Dec 2020 23:16:07 +0100</pubDate><guid>https://glaforge.dev/posts/2020/12/02/day-2-with-workflows-a-workflow-is-made-of-steps-and-jumps/</guid><description>&lt;p>Let&amp;rsquo;s continue our discovery of &lt;a href="https://cloud.google.com/workflows">Goole Cloud Workflows&lt;/a>!&lt;/p>
&lt;p>Yesterday, we discovered the UI of Workflows.
We &lt;a href="https://glaforge.dev/posts/2020/12/01/day-1-with-workflows-your-first-step-to-hello-world/">created our first workflow&lt;/a>.
We started with a single step, returning a greeting message:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>- &lt;span style="color:#062873;font-weight:bold">sayHello&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">    &lt;/span>&lt;span style="color:#062873;font-weight:bold">return&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Hello from Cloud Workflows!&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A workflow definition is made of steps.
But not just one! You can create several steps.
In YAML, the structure of your workflow will be something like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>- &lt;span style="color:#062873;font-weight:bold">stepOne&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">    &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># do something&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>- &lt;span style="color:#062873;font-weight:bold">stepTwo&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">    &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># do something else&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>- &lt;span style="color:#062873;font-weight:bold">sayHello&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">    &lt;/span>&lt;span style="color:#062873;font-weight:bold">return&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Hello from Cloud Workflows!&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>By default, steps are executed in the order they appear, from top to bottom.
The execution will finish when either you return a value, or you reach the final step.
If there&amp;rsquo;s no return statement, a null value is returned as result of the workflow execution.&lt;/p></description><content:encoded>
<![CDATA[<p>Let&rsquo;s continue our discovery of <a href="https://cloud.google.com/workflows">Goole Cloud Workflows</a>!</p>
<p>Yesterday, we discovered the UI of Workflows.
We <a href="https://glaforge.dev/posts/2020/12/01/day-1-with-workflows-your-first-step-to-hello-world/">created our first workflow</a>.
We started with a single step, returning a greeting message:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">sayHello</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb"> </span>Hello from Cloud Workflows!<span style="color:#bbb">
</span></span></span></code></pre></div><p>A workflow definition is made of steps.
But not just one! You can create several steps.
In YAML, the structure of your workflow will be something like:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">stepOne</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic"># do something</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">stepTwo</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic"># do something else</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">sayHello</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb"> </span>Hello from Cloud Workflows!<span style="color:#bbb">
</span></span></span></code></pre></div><p>By default, steps are executed in the order they appear, from top to bottom.
The execution will finish when either you return a value, or you reach the final step.
If there&rsquo;s no return statement, a null value is returned as result of the workflow execution.</p>
<p>A small step for a workflow execution, but you can also do a jump between steps!
For that, you&rsquo;ll use the next instruction:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">stepOne</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">next</span>:<span style="color:#bbb"> </span>stepTwo<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">stepThree</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">next</span>:<span style="color:#bbb"> </span>sayHello<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">stepTwo</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">next</span>:<span style="color:#bbb"> </span>stepThree<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">sayHello</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb"> </span>Hello from Cloud Workflows!<span style="color:#bbb">
</span></span></span></code></pre></div><p>Here, we jump between steps, back and forth, before going to the final step that will return a value, and thus finish the execution of our workflow.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/BTzb1m5pDXI?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>Of course, we can go beyond a linear series of steps, and in subsequent articles,
we&rsquo;ll see how we can create conditional jumps and switches, for more complex logic,
and how we can pass some data and values between steps.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Day 1 with Workflows — Your first step to Hello World</title><link>https://glaforge.dev/posts/2020/12/01/day-1-with-workflows-your-first-step-to-hello-world/</link><pubDate>Tue, 01 Dec 2020 23:22:11 +0100</pubDate><guid>https://glaforge.dev/posts/2020/12/01/day-1-with-workflows-your-first-step-to-hello-world/</guid><description>&lt;p>With more and more interconnected services, making sense of their interactions becomes critical.
With Google &lt;a href="https://cloud.google.com/workflows">Cloud Workflows&lt;/a>,
developers can orchestrate and automate such complex systems by creating serverless workflows.&lt;/p>
&lt;p>In this series of articles, we will learn together how to use Goole Cloud Workflows,
and get to know all its features, with short and easy to read tutorials.
For our first day, we&amp;rsquo;ll discover and use the Workflows UI in the
&lt;a href="https://console.cloud.google.com/">cloud console&lt;/a>.
We will create a simple &amp;ldquo;hello world&amp;rdquo; workflow, consisting of a simple step.
Going further, in the coming days, we&amp;rsquo;ll learn more about advanced features.
But first things first!&lt;/p></description><content:encoded>
<![CDATA[<p>With more and more interconnected services, making sense of their interactions becomes critical.
With Google <a href="https://cloud.google.com/workflows">Cloud Workflows</a>,
developers can orchestrate and automate such complex systems by creating serverless workflows.</p>
<p>In this series of articles, we will learn together how to use Goole Cloud Workflows,
and get to know all its features, with short and easy to read tutorials.
For our first day, we&rsquo;ll discover and use the Workflows UI in the
<a href="https://console.cloud.google.com/">cloud console</a>.
We will create a simple &ldquo;hello world&rdquo; workflow, consisting of a simple step.
Going further, in the coming days, we&rsquo;ll learn more about advanced features.
But first things first!</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/75BekrpL-qo?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>In the Google Cloud console UI, you can locate Workflows in the <code>Tools</code> section of the hamburger menu:</p>
<p><figure>
  <a href="#img-60149b082263ff11f1ae2750f2490229">
    <img src="/img/workflows-days/wf01-01-menu.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-60149b082263ff11f1ae2750f2490229">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-days/wf01-01-menu.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>You can pin this menu item, to have it stick at the top of the list.</p>
<p>The first time you are accessing this page, you&rsquo;ll be greeted with the following screen,
which will ask you to enable the Workflows API. So just click on <code>ENABLE</code>:</p>
<p><figure>
  <a href="#img-dc1f38ca7ed02022389744b18e2f5400">
    <img src="/img/workflows-days/wf01-02-enable-api.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-dc1f38ca7ed02022389744b18e2f5400">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-days/wf01-02-enable-api.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Once the API is enabled, you&rsquo;ll be in the Workflows home screen:</p>
<p><figure>
  <a href="#img-f1e80d1bfc330fec34744645ab091128">
    <img src="/img/workflows-days/wf01-03-workflows-home.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-f1e80d1bfc330fec34744645ab091128">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-days/wf01-03-workflows-home.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Click on the <code>CREATE</code> button to create your first workflow definition:</p>
<p><figure>
  <a href="#img-6a1bb88ece1ed976a0037f05325e4975">
    <img src="/img/workflows-days/wf01-04-empty-state.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-6a1bb88ece1ed976a0037f05325e4975">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-days/wf01-04-empty-state.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Find a name for your workflow (it should start with a letter).
You can provide an optional description. Currently, only &ldquo;us-central1&rdquo;
is available for the beta of Workflows, but more regions will be available later on.</p>
<p>Notice that we have to select a service account that Workflows will use to call other Google Cloud APIs, however here, there&rsquo;s a warning telling us that the project requires a service account.
As I&rsquo;ve created a brand new project, I didn&rsquo;t have any service account created.
If you had used, for example, Cloud Functions beforehand, a default service account would have been created. If you need to create a service account,
you can create one in <code>IAM &amp; Admin &gt; Service Accounts</code>, then use this one.</p>
<p>My first workflow will be called <code>w01-first-workflow</code>:</p>
<p><figure>
  <a href="#img-509c9b6d1c89ed9c28434a4a3a88391f">
    <img src="/img/workflows-days/wf01-05-form-filled.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-509c9b6d1c89ed9c28434a4a3a88391f">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-days/wf01-05-form-filled.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Move on to the next section with the <code>NEXT</code> button.
That&rsquo;s where you will define your workflow:</p>
<p><figure>
  <a href="#img-c0ee888a16245a5a70d46f46ba356db3">
    <img src="/img/workflows-days/wf01-06-definition.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-c0ee888a16245a5a70d46f46ba356db3">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-days/wf01-06-definition.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>This first workflow consists in one single step, called <code>sayHello</code>,
and whose sole purpose is to return a hello world message:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">sayHello</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb"> </span>Hello from Cloud Workflows!<span style="color:#bbb">
</span></span></span></code></pre></div><p>As you can see, workflow definitions are written using the
<a href="https://yaml.org/">YAML</a> configuration language.</p>
<p>Click <code>DEPLOY</code> to deploy the workflow. You will then see the details of your new workflow.
In the &ldquo;executions&rdquo; tab, you can see past executions.</p>
<p><figure>
  <a href="#img-81646faa8d91ac8a075f774f2922785c">
    <img src="/img/workflows-days/wf01-07-created.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-81646faa8d91ac8a075f774f2922785c">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-days/wf01-07-created.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>In the <code>logs</code> section, you can see the logging messages associated with your workflow creation, deployment and executions:</p>
<p><figure>
  <a href="#img-0c2cbbcd13ad04cf497516748473a757">
    <img src="/img/workflows-days/wf01-08-logs.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-0c2cbbcd13ad04cf497516748473a757">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-days/wf01-08-logs.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>And in the <code>definitions</code> section, you can see the YAML description you just created:</p>
<p><figure>
  <a href="#img-71178e72cf21aa29b0a7428058bb499d">
    <img src="/img/workflows-days/wf01-09-definition.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-71178e72cf21aa29b0a7428058bb499d">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-days/wf01-09-definition.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Now click on the <code>EXECUTE</code> button. You will see the input section
(we&rsquo;ll learn about input arguments in an upcoming article), and the YAML definition.
Click the other <code>EXECUTE</code> button:</p>
<p><figure>
  <a href="#img-b86caa1790e97997d68b79a3a114d094">
    <img src="/img/workflows-days/wf01-10-exec-screen.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-b86caa1790e97997d68b79a3a114d094">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-days/wf01-10-exec-screen.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>You will see the result of your execution (<code>succeeded</code>, with other details about the execution),
as well as both the input and the output, with our greeting message:</p>
<p><figure>
  <a href="#img-b87219fa2fc75baf9936a6f1d88d1705">
    <img src="/img/workflows-days/wf01-11-executed.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-b87219fa2fc75baf9936a6f1d88d1705">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/workflows-days/wf01-11-executed.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>And here you are! You created your first workflow definition, and launched the first execution of this workflow!</p>
<p>In the coming days, we will have a closer look at the structure of workflow definitions (its steps),
how to define input arguments, but also how to create an execution of a workflow from the command-line.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Orchestrating microservices with Google Cloud Workflows</title><link>https://glaforge.dev/posts/2020/11/18/orchestrating-microservices-with-cloud-workflows/</link><pubDate>Wed, 18 Nov 2020 18:56:08 +0100</pubDate><guid>https://glaforge.dev/posts/2020/11/18/orchestrating-microservices-with-cloud-workflows/</guid><description>&lt;p>The trend toward splitting a monolith into fine-grained loosely-coupled microservices has its merits.
It allows us to scale parts of an application more easily. Teams become more effective on their focused perimeter.
However, in a chain or graph of services interacting with each other via message buses or other eventing mechanisms,
it becomes difficult to understand when things start to break.
Your business processes spanning those services are in limbo.
Here starts detective work to find out how to get back on track.&lt;/p></description><content:encoded>
<![CDATA[<p>The trend toward splitting a monolith into fine-grained loosely-coupled microservices has its merits.
It allows us to scale parts of an application more easily. Teams become more effective on their focused perimeter.
However, in a chain or graph of services interacting with each other via message buses or other eventing mechanisms,
it becomes difficult to understand when things start to break.
Your business processes spanning those services are in limbo.
Here starts detective work to find out how to get back on track.</p>
<p><strong>Choreography</strong>: like a bunch of dancers on the floor composing a ballet.
Loosely-coupled microservices compose business processes without really being aware of each other,
casually interacting by receiving and sending messages or events.</p>
<p><strong>Orchestration</strong>: more like a conductor of an orchestra who directs musicians and their instruments to play each part.
The approach of using a higher level solution that purposefully invokes and tracks each individual service,
enables developers to know what the current state of a business process is.</p>
<p>Both approaches have their pros and cons. The loosely-coupled aspects of choreography certainly enables agility.
But business processes are harder to follow.
Although orchestration adds a single-point-of-failure with its orchestrator tying all the pieces together,
it brings clarity in the spaghetti of myriads of microservices.</p>
<p>In addition to GCP&rsquo;s existing messaging (<a href="https://cloud.google.com/pubsub/">Cloud Pub/Sub</a>)
and eventing solutions (<a href="https://cloud.google.com/blog/products/serverless/build-event-driven-applications-in-cloud-run">Eventarc</a>)
for your service choreography, the newly launched product <a href="https://cloud.google.com/workflows">Cloud Workflows</a> 
is tackling the orchestration approach.</p>
<p>Cloud Workflows is a scalable fully-managed serverless system that automates and coordinates services,
takes care of error handling and retries on failure, and tells you if the overall process has finished.</p>
<p>In this short video, during the &ldquo;demo derby&rdquo; at Google Cloud Next OnAir,
I had the chance to present a demo of Cloud Workflows, with some concrete examples:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/_4fo_u5rY_8?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>In this video, I started with the proverbial Hello World, using the Yaml syntax for defining workflows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">hello</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Hello from Cloud Workflows!&#34;</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>I defined a <code>hello</code> step, whose sole purpose is to return a string, as the result of its execution.</p>
<p>Next, I showed that workflow definitions can take arguments, and also return values thanks to more complex expressions:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">main</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">params</span>:<span style="color:#bbb"> </span>[args]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">steps</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">returnGreeting</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb"> </span>${&#34;Hello &#34; + args.first + &#34; &#34; + args.last}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Cloud Workflows is able to invoke any HTTP-based service (and supports OAuth2 and OIDC),
whether in Google Cloud or outside (on premises, or other servers).
Here, I invoke 2 Cloud Functions:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">getRandomNumber</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>http.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb"> </span>https://us-central1-myprj.cloudfunctions.net/randomNumber<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>randomNumber<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">getNthPoemVerse</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>http.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb"> </span>https://us-central1-myprj.cloudfunctions.net/theCatPoem<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">query</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">nth</span>:<span style="color:#bbb"> </span>${randomNumber.body.number}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>randomVerse<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">returnOutput</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb"> </span>${randomVerse.body}<span style="color:#bbb">
</span></span></span></code></pre></div><p>The <code>getRandomNumber</code> step calls a function that returns a random number with an HTTP GET,
and stores the result of that invocation in the <code>randomNumber</code> variable.</p>
<p>The <code>getNthPoemVerse</code> calls another function that takes a query parameter,
which is found in the <code>randomNumber</code> variable which holds the result of the previous function invocation.</p>
<p>The <code>returnOutput</code> step then returns the resulting value.</p>
<p>My fourth example shows variable assignment and conditional switches in action:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#062873;font-weight:bold">getRandomNumber</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">call</span>:<span style="color:#bbb"> </span>http.get<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">args</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">url</span>:<span style="color:#bbb"> </span>https://us-central1-myprj.cloudfunctions.net/randomNumber<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">result</span>:<span style="color:#bbb"> </span>randomNumber<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">assign_vars</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">assign</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">number</span>:<span style="color:#bbb"> </span>${int(randomNumber.body.number)}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">conditionalSwitch</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">switch</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">condition</span>:<span style="color:#bbb"> </span>${number &lt; 33}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">next</span>:<span style="color:#bbb"> </span>low<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#062873;font-weight:bold">condition</span>:<span style="color:#bbb"> </span>${number &lt; 66}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">next</span>:<span style="color:#bbb"> </span>medium<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">next</span>:<span style="color:#bbb"> </span>high<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">low</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb"> </span>${&#34;That&#39;s pretty small! &#34; + string(number)}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">medium</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb"> </span>${&#34;Hmm, okay, an average number. &#34; + string(number)}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#062873;font-weight:bold">high</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">return</span>:<span style="color:#bbb"> </span>${&#34;It&#39;s a big number! &#34; + string(number)}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Reusing the random function from the previous example, notice how variables are assigned,
and how to create a switch with multiple conditions,
as well as showing how to redirect the execution of the workflow to different steps, depending on the outcome of the switch.</p>
<p>But there&rsquo;s really more to this! You can double check the <a href="https://cloud.google.com/workflows/docs/reference/syntax">syntax reference</a>,
to see all the constructs you can use in your workflow definitions.</p>
<h3 id="summary">Summary</h3>
<p>Cloud Workflows:</p>
<ul>
<li>Orchestrate Google Cloud and HTTP-based API services into serverless workflows</li>
<li>Automate complex processes</li>
<li>Fully managed service requires no infrastructure or capacity planning</li>
<li>Fast scalability supports scaling down to zero and pay-per-use pricing model</li>
</ul>
<p>In terms of features:</p>
<ul>
<li>Reliable workflow execution</li>
<li>Built-in error handling</li>
<li>Passing variable values between workflow steps</li>
<li>Built-in authentication for Google Cloud products</li>
<li>Low latency of execution</li>
<li>Support for external API calls</li>
<li>Built-in decisions and conditional step executions</li>
<li>Cloud Logging</li>
</ul>
<p>If you want to get started with <a href="https://cloud.google.com/workflows">Cloud Workflows</a>, you can head over to this <a href="https://codelabs.developers.google.com/codelabs/cloud-workflows-intro#0">hands-on codelabs</a> from my colleague <a href="https://twitter.com/meteatamel">Mete Atamel</a>. Learn more by watching this <a href="https://www.youtube.com/watch?v=Uz8G8fTwwXs">longer video</a> by Product Manager Filip Knapik who dives into Cloud Workflows. In upcoming articles, we&rsquo;ll come back to Workflows into more details, diving into some more advanced features, or how to migrate a choreographed example, into an orchestrated one. So, stay tuned!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>The developer advocacy feedback loop</title><link>https://glaforge.dev/talks/2020/08/06/the-developer-advocacy-feedback-loop/</link><pubDate>Thu, 06 Aug 2020 19:11:17 +0100</pubDate><guid>https://glaforge.dev/talks/2020/08/06/the-developer-advocacy-feedback-loop/</guid><description>&lt;p>For one of the &lt;a href="https://2020.devrel.net/speaker/guillaume-laforge/">closing keynotes&lt;/a> of &lt;a href="https://2020.devrel.net/">DevRelCon Earth 2020&lt;/a>, I spoke about what I call the Developer Advocacy Feedback Loop. People often think about developer relations and advocacy as just being about external outreach. However, there&amp;rsquo;s more to it! Developer Advocates are here to represent users, developers, technical practitioners, to influence the roadmap and development of the services and products to suit their needs. That&amp;rsquo;s the internal advocacy that loops back into improving the products.&lt;/p></description><content:encoded>
<![CDATA[<p>For one of the <a href="https://2020.devrel.net/speaker/guillaume-laforge/">closing keynotes</a> of <a href="https://2020.devrel.net/">DevRelCon Earth 2020</a>, I spoke about what I call the Developer Advocacy Feedback Loop. People often think about developer relations and advocacy as just being about external outreach. However, there&rsquo;s more to it! Developer Advocates are here to represent users, developers, technical practitioners, to influence the roadmap and development of the services and products to suit their needs. That&rsquo;s the internal advocacy that loops back into improving the products.</p>
<p>Without further ado, let me share with you the slide deck here:</p>
<script async class="speakerdeck-embed" data-id="c9aa5be6e7534443963a5b981ef0b713" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>Let me paraphrase what I presented in this talk.</p>
<p>For the past 4 years, I&rsquo;ve been a Developer Advocate, for Google, focusing on Google Cloud, and especially our serverless solutions (like App Engine, Cloud Functions, Cloud Run). I fell in the magic potion of advocacy, inadvertently, a long time ago while working on an open source project. This project is the <a href="http://groovy-lang.org/">Apache Groovy</a> programming language. I was leading the project, but at the same time, I was also evangelising it at events, through articles,, and was trying to incorporate the feedback I was getting in the field back into the project. I was doing advocacy without really realizing it, like <a href="https://literature.stackexchange.com/questions/11844/meaning-of-the-prose-of-monsieur-jourdain">Mr Jourdain</a> in Molière&rsquo;s play who was speaking in prose without knowing it. But I really saw a loop, a feedback loop, in the process, in how you spread the word about technologies, but also how you can listen to the feedback and improve your product.</p>
<p><figure>
  <a href="#img-376110755c13ec92e741087841ee6559">
    <img src="/img/developer-advocacy-feedback-loop/01&#43;-&#43;feedback&#43;loop.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-376110755c13ec92e741087841ee6559">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/01&#43;-&#43;feedback&#43;loop.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>If you&rsquo;ve studied Electronics, you might have seen such diagrams about the feedback loop. There&rsquo;s something in input, something in output, but there&rsquo;s a loop back, that brings some output back into the input channel. To make the parallel with advocacy&hellip; Advocacy is not just a one-way monologue, it&rsquo;s a conversation: you&rsquo;re here to tell a story to your kids for example, but you listen to feedback from the audience, on how to make your story even better. Not just how you tell the story (better intonation, pauses), but really improving the plot, the characters, the setting,≈‹ everything, perhaps making up a totally different story in the end!</p>
<p><figure>
  <a href="#img-440569dbe95aa7bea414b513d8c71f21">
    <img src="/img/developer-advocacy-feedback-loop/03&#43;-&#43;devrel&#43;multitude.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-440569dbe95aa7bea414b513d8c71f21">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/03&#43;-&#43;devrel&#43;multitude.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Let me start with a short disclaimer. If you ask this room to give a definition of developer relations, or developer advocacy, or evangelism (a term I avoid because of its connotations), you&rsquo;ll get as many answers as there are attendees. I don&rsquo;t claim I have THE ultimate definitions for these concepts and approaches. I don&rsquo;t claim those things are the same things, or are different. And anyway, there&rsquo;s not just one way to do it, there&rsquo;s a multitude of ways. They are many things we do the same way, but I&rsquo;m sure there are many incredible things you do that I&rsquo;m not even aware of but that I&rsquo;d like to learn more about! But I&rsquo;ll tell you how I am doing developer advocacy, and where this feedback loop comes into play.</p>
<p><figure>
  <a href="#img-45ecc9c0db6af982484863f08cb0d693">
    <img src="/img/developer-advocacy-feedback-loop/02&#43;-&#43;who&#43;are&#43;we.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-45ecc9c0db6af982484863f08cb0d693">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/02&#43;-&#43;who&#43;are&#43;we.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>So, who are we? DevRel is not always the same thing everywhere, in every company. And there&rsquo;s not just one way to do DevRel.</p>
<p>Are we salespeople? Not necessarily, I don&rsquo;t get any bucks when I indirectly help sign a new customer deal. My metrics are more about the number of developers reached, or views on my articles or videos, or Twitter impressions.</p>
<p>So are we marketing people? Well, I have some similar metrics for sure, I advertise the products or company I represent, but my goal is that my audience (the technical practitioners) be successful, even if they end up not using my technology. I want my audience to even advocate themselves for those products if possible (if the product is good and makes sense for them).</p>
<p>Are we engineers? In my case, yes I am, I&rsquo;m even in the Engineering org chart, and to show more empathy towards our engineer users, it&rsquo;s easier if we&rsquo;re engineers ourselves. We speak the same language. We&rsquo;re part of the same community. We have the same tool belt. Also as an engineer, I can even sometimes contribute to the products I talk about. But it&rsquo;s not because you&rsquo;re not an engineer that you can&rsquo;t succeed, and be a good advocate! Empathy is really key in this role, more so probably than engineering chops.</p>
<p>Or are we PMs? In a previous life, in a small startup, I was actually wearing 2 hats: PM &amp; DA. But it&rsquo;s tough to do two jobs like these at the same time. As a DA (without being a PM), with my contributions, my feedback from the field, from the community I advocate for, I do influence the roadmap of our products, for sure. But I&rsquo;m only a part of the equation. However providing critical product feedback is super important in my job. That&rsquo;s the key aspect of the developer advocacy feedback loop!</p>
<p>Perhaps we&rsquo;re just international travelers? We&rsquo;re measured by the number of visa stamps on our passports? Ah well, maybe. Or maybe not, we try to be greener, but with COVID-19, things have changed recently! The pandemic refines our job, our duties, our ways to communicate. There&rsquo;s lots we can do in the comfort of our home office too.</p>
<p>Ultimately, we&rsquo;re all different, but we all have myriads of ways to contribute and reach our goal. Some of us may be focusing more on awesome videos tutorials, some on organizing hours-long hackathons, and others will be awesome beta-testers for our products, write cristal-clear code samples or SDKs, etc. There&rsquo;s not just one way to be a great Developer Advocate. You don&rsquo;t need to do it all. And we&rsquo;re a team. So we complement each other with our respective strengths. And we work with others too, like marketing, sales, consulting, tech writers, leadership.</p>
<p><figure>
  <a href="#img-d24a02d2318f4f4c8f86087620a0c174">
    <img src="/img/developer-advocacy-feedback-loop/04&#43;-&#43;our&#43;goal.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-d24a02d2318f4f4c8f86087620a0c174">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/04&#43;-&#43;our&#43;goal.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>What do we do, what&rsquo;s our goal? We are empowering our users to reach their goals. We want to make them successful. We&rsquo;re enabling customer success. We&rsquo;re driving mindshare in the field, in our communities. We are making our users happy!</p>
<p><figure>
  <a href="#img-e74dd89e8d2067cda3997db28b3a0d39">
    <img src="/img/developer-advocacy-feedback-loop/05&#43;-&#43;our&#43;tools.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-e74dd89e8d2067cda3997db28b3a0d39">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/05&#43;-&#43;our&#43;tools.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>How do we make our community, our users, our customers be successful? There are many tools for that. Some of the most well-known tools that we can use are outward facing: it&rsquo;s about external outreach (talks, articles, videos, etc.) But to make our communities more successful, we also need to get our products improved. That&rsquo;s where we create the feedback loop, with our internal influence, thanks to some tools I&rsquo;ll enumerate, we can help make the products better, by bringing our users&rsquo; feedback up the chain to the PMs, Product Leads, etc. Let me show you.</p>
<p><figure>
  <a href="#img-ada68f15f833cffb5caa977dfc0e2d56">
    <img src="/img/developer-advocacy-feedback-loop/06&#43;-&#43;the&#43;advocacy&#43;loop.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-ada68f15f833cffb5caa977dfc0e2d56">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/06&#43;-&#43;the&#43;advocacy&#43;loop.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Let me introduce you to our personas of my story, of my feedback loop.</p>
<p><figure>
  <a href="#img-1e5bc58f4bff39a778dedbff0a3fc1f2">
    <img src="/img/developer-advocacy-feedback-loop/07&#43;-&#43;the&#43;personnas.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-1e5bc58f4bff39a778dedbff0a3fc1f2">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/07&#43;-&#43;the&#43;personnas.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>At the top, you have the product leadership, the PM, CxOs, the SWEs. At the bottom, that&rsquo;s our users, our customers, our technical practitioners And in the middle, in between, there&rsquo;s you, the Developer Advocate.</p>
<p><figure>
  <a href="#img-0291b7e4fd994c0c4836dfc534975b57">
    <img src="/img/developer-advocacy-feedback-loop/08&#43;-&#43;company.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-0291b7e4fd994c0c4836dfc534975b57">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/08&#43;-&#43;company.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>But in a way, there are two teams. Here, in the white cloud, at the top, that&rsquo;s your company.</p>
<p><figure>
  <a href="#img-d6fa04b397fabf5cdebeca4839e24e53">
    <img src="https://glaforge.appspot.com/media/09&#43;-&#43;community.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-d6fa04b397fabf5cdebeca4839e24e53">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://glaforge.appspot.com/media/09&#43;-&#43;community.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>But at the bottom, that&rsquo;s your community, with your users. You&rsquo;re not just part of the company, you&rsquo;re also part of the community. You are the advocate for your users, representing them to the product leadership, so that their voice is being heard!</p>
<p><figure>
  <a href="#img-64bf38d5486fe42f0e3f023c32dbd1de">
    <img src="/img/developer-advocacy-feedback-loop/10&#43;-&#43;external&#43;outreach.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-64bf38d5486fe42f0e3f023c32dbd1de">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/10&#43;-&#43;external&#43;outreach.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>That&rsquo;s the external outreach. What some call evangelism, the outward part of the loop. You&rsquo;re the voice of the company. You spread the word on your cool technology. You&rsquo;re creating great demos, code samples, polished videos. You&rsquo;re writing helpful articles, useful tutorials, readable documentation. You&rsquo;re attending and presenting at events to talk about the products. You&rsquo;re helping users succeed by answering questions on social media, StackOverflow, or other forums.</p>
<p><figure>
  <a href="#img-d3c22faa69fba5dfc1003df2e27c7fe6">
    <img src="/img/developer-advocacy-feedback-loop/11&#43;-&#43;internal&#43;advocacy.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-d3c22faa69fba5dfc1003df2e27c7fe6">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/11&#43;-&#43;internal&#43;advocacy.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>What makes it a feedback loop is this part. It&rsquo;s not just a by-product of the external outreach. It&rsquo;s an integral part of the advocacy work. There&rsquo;s the obvious stuff like filing bugs, or being a customer zero by testing the product before it launches. But things like writing trip reports, friction logs, customer empathy sessions may be new to you. If you can, make it a habit to produce such artifacts. And you can list, and track, and report about all those feedback elements that you bring upstream, and check how it&rsquo;s being enacted or not.</p>
<p>Often people think about us mostly for the outreach part, the arrow going downward toward our community. They can think we&rsquo;re just kind of marketing puppets. And I&rsquo;ve seen conference organisers complaining they only got &ldquo;evangelists&rdquo; at their show, when they wanted &ldquo;real engineers&rdquo; instead, working on the products or projects. But frankly, they are not necessarily always the best at explaining their own projects! Folks often forget that we&rsquo;re here to make them successful, and report their feedback, their needs, to advocate for them, and to influence the decision makers to make better products that fill the needs of those users. Both parts are critical! And please pay attention to that feedback loop, to that arrow going back to the top of the slide, to the leadership.</p>
<p>So let&rsquo;s see some concrete examples of the things you can put in place to provide feedback, and show that DevRel is important and has a strong impact.</p>
<p><figure>
  <a href="#img-9a825926c49d64cb60d021fdc3327855">
    <img src="/img/developer-advocacy-feedback-loop/12&#43;-&#43;friction&#43;logs.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-9a825926c49d64cb60d021fdc3327855">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/12&#43;-&#43;friction&#43;logs.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>To make developers happy, you need to remove as much friction as possible. You want the developer experience to be as smooth as possible. You might need to work with UX designers and PMs directly for that. But you can also report about your findings, where you saw friction by writing a friction log. Last week, my colleague Emma <a href="https://www.youtube.com/watch?v=765wLWVcyS0">spoke about this</a> at DevRelCon Earth, and another great colleague, Aja, wrote about <a href="https://devrel.net/developer-experience/an-introduction-to-friction-logging">friction logging</a> on DevRel.net a while ago. Great resources to check out!</p>
<p><figure>
  <a href="#img-b764d65ee0958f30975c49ce3048f844">
    <img src="/img/developer-advocacy-feedback-loop/13&#43;-&#43;example&#43;friction&#43;log.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-b764d65ee0958f30975c49ce3048f844">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/13&#43;-&#43;example&#43;friction&#43;log.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I&rsquo;m going to show you a real friction log. There&rsquo;s some metadata about the environment, date, user name, scenario title, etc. You&rsquo;re reporting about some concrete use case you were trying to implement (an app you were building, a new API you were trying to use, etc.) You document all the steps you followed, and tell what worked or not, how you expected things to work out. This document will be shared broadly via an alias which pings most PMs, tech leads, etc. So it&rsquo;s very visible.</p>
<p><figure>
  <a href="#img-0b3594fccdf53abf6639dbc4ed6acf9b">
    <img src="/img/developer-advocacy-feedback-loop/14&#43;-&#43;friction&#43;log&#43;color&#43;coding.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-0b3594fccdf53abf6639dbc4ed6acf9b">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/14&#43;-&#43;friction&#43;log&#43;color&#43;coding.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>But the key thing here is the color coding aspect. You show where there&rsquo;s friction, where there&rsquo;s frustration, where you&rsquo;d quit if you were a real user. But also, it&rsquo;s super important to highlight what worked well, what surprised you, what delighted you. It&rsquo;s not just about the negative things.</p>
<p><figure>
  <a href="#img-21780f32de53a35a5204ebc93ee18e42">
    <img src="/img/developer-advocacy-feedback-loop/15&#43;-&#43;tagging&#43;stakeholders.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-21780f32de53a35a5204ebc93ee18e42">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/15&#43;-&#43;tagging&#43;stakeholders.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>And the last trick to make it effective: add comments, tagging key stakeholders (PMs, Tech Leads, etc), so they really acknowledge the problem. Create associated bug requests, and track them to check if progress is made.</p>
<p><figure>
  <a href="#img-32910b0e9e2c7dd5fbbf5601a3a726dd">
    <img src="/img/developer-advocacy-feedback-loop/16&#43;-&#43;friction&#43;vlog.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-32910b0e9e2c7dd5fbbf5601a3a726dd">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/16&#43;-&#43;friction&#43;vlog.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>My colleague <a href="https://twitter.com/ZackAkil">Zack</a> even developed an application for creating friction vlogs. Video friction logs! With a video, you can show concretely your frustration (but perhaps don&rsquo;t swear too much). A video shows where you struggle, where you lose time. You can navigate to various sections in the video, and annotate those sections, with the green / orange / red color coding scheme. The tool also creates a classical written friction log document as well. I found that application pretty neat, to be honest, especially as it also shows where users struggle, where they lose time.</p>
<p><figure>
  <a href="#img-e8f56fddb0a4deb81116871d7f6be4fa">
    <img src="/img/developer-advocacy-feedback-loop/17&#43;-&#43;advocacy&#43;reporting.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-e8f56fddb0a4deb81116871d7f6be4fa">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/17&#43;-&#43;advocacy&#43;reporting.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>You can apply the same approach to other kinds of reporting activities. We often write reports for our trips, events, meetups, customer engagements. In particular, although we&rsquo;re not sales people, we&rsquo;re trying to show that we also have an impact on sales. Customers love having DevRel people come and show cool stuff! And we can collect and show the feedback coming from the field to the leadership. It&rsquo;s not just us sharing our own impressions and ideas, it&rsquo;s really coming from someone else&rsquo;s mouth, so it has more weight in the conversation. I&rsquo;d like to highlight our internal advocacy reporting: we have someone on the team that collects all our bug reports (and included them in bug hotlists), all our friction logs, our trip reports, and who actively tracks how this feedback is taken into account, and it&rsquo;s a very effective way of showing that we do have impact to the leadership, beyond the usual metrics. And by the way, even those DevRel product feedback reports make use of the color coding we have in friction logs. So it&rsquo;s a very familiar thing for all our engineering team.</p>
<p><figure>
  <a href="#img-756f63e3eeebd98514eb10432b01f74d">
    <img src="/img/developer-advocacy-feedback-loop/18&#43;-&#43;customer&#43;empathy&#43;sessions.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-756f63e3eeebd98514eb10432b01f74d">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/18&#43;-&#43;customer&#43;empathy&#43;sessions.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Another interesting thing we&rsquo;re running is what we call customer empathy sessions, a concept invented by my colleague <a href="https://twitter.com/kmbannerman">Kim</a>. Gather various PMs, SWEs, DevRel people, potentially customers but not mandatory, in the same room (or virtually nowadays) and craft some concrete scenarios of something you&rsquo;d like them to build in small groups (but where you know there&rsquo;s gonna be lots of friction). With teams of 3 or more, each one has a role: a driver, a scribe, and a searcher. Have them do the task. Then compare copies at the end. It&rsquo;s a bit like creating new Critical User Journeys that have not been addressed, that exhibit a lot of friction. But this time the engineers, the PM, will really feel the very same frustration our customers can potentially feel when they can&rsquo;t accomplish their tasks. The various teams often work in silos, on a particular aspect, and avoid certain paths (when you know you shouldn&rsquo;t click somewhere, you won&rsquo;t do it, you&rsquo;ll use the other path you know works). But customer empathy sessions are here to show what our users have to go through in real scenarios, beyond a handful of critical journeys. In summary, feel the pain, and show empathy toward your customers! Really, I won&rsquo;t stress this enough, but empathy is key here, and a real driver for positive change.</p>
<p><figure>
  <a href="#img-8041af831632f909b898a36206cde24a">
    <img src="/img/developer-advocacy-feedback-loop/19&#43;-&#43;hallway&#43;track&#43;office&#43;hours.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-8041af831632f909b898a36206cde24a">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/19&#43;-&#43;hallway&#43;track&#43;office&#43;hours.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>We can do scalable advocacy, by creating things like videos that are broadcasted to many thousands of watchers. That have a long shelf time, which is less ephemeral than a conference talk. But sometimes, it&rsquo;s also good to do things that actually don&rsquo;t scale. Helping one single person can make a big difference: at a conference after my talk, I had a long conversation with an attendee that had a particular need. I onboarded them on our early access program for a new product, which seemed to be what they needed. They could provide key feedback to our PMs and engineers. And they helped us get that new product ready with a real use case. And the next year, the attendee was a key customer, that even came on stage to talk about the product. So I both won a new customer and a new advocate for that product. So the hallway track at events is very important. And that&rsquo;s the kind of feedback signals I&rsquo;m missing in those times of pandemic.</p>
<p>Another approach is office hours: you set up some time slots in your calendar, and anyone can book time with you. That&rsquo;s a great way to get feedback, and see what problems users are facing. I haven&rsquo;t tried that myself, as I&rsquo;m a bit shy, and afraid someone would ask questions on topics I don&rsquo;t know much about! But that&rsquo;s very effective, and I have several colleagues doing that, and who are learning along the way.</p>
<p><figure>
  <a href="#img-a265a4f94b8b37c5772ad5277ea76d2c">
    <img src="/img/developer-advocacy-feedback-loop/20&#43;-&#43;create&#43;new&#43;products.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-a265a4f94b8b37c5772ad5277ea76d2c">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/20&#43;-&#43;create&#43;new&#43;products.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Sometimes, your community, your users, will highlight a missing gap in your product portfolio. And it might give you some ideas of a product that would delight those persons, and they could become customers if you had that product. So that&rsquo;s actually how some of my colleagues went on creating totally new products, for example for gaming companies, or for secret management. On another occasion, as I had strong ideas on how a new product runtime should look like, I went on designing and prototyping an API that our users would use. Somehow, it&rsquo;s a bit like being the change you want to see in the world! And the API I designed, further improved with the engineering team, is now an API our customers are using today.</p>
<p><figure>
  <a href="#img-503378d1a9c659bd4ef6b9f776e76d86">
    <img src="/img/developer-advocacy-feedback-loop/21&#43;-&#43;pudding.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-503378d1a9c659bd4ef6b9f776e76d86">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/21&#43;-&#43;pudding.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Time to wrap up. Often, the proof is in the pudding. It&rsquo;s not just about our intuitions or own personal experience. You need to gather feedback, in particular concrete customer feedback, to prove that you&rsquo;re right. And when it&rsquo;s a customer with some money to spend, usually product leadership listens.</p>
<p><figure>
  <a href="#img-f3bcceb9a0fa8a06c4e52140d58cb477">
    <img src="/img/developer-advocacy-feedback-loop/22&#43;-&#43;roses.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-f3bcceb9a0fa8a06c4e52140d58cb477">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/22&#43;-&#43;roses.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Sometimes, it&rsquo;s all roses and bloom! Our feedback, ideas, features are implemented! Woohoo! Success!</p>
<p><figure>
  <a href="#img-46cb6bdcdb21fbb48dfac96daae74a85">
    <img src="/img/developer-advocacy-feedback-loop/23&#43;-&#43;brick&#43;wall.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-46cb6bdcdb21fbb48dfac96daae74a85">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/23&#43;-&#43;brick&#43;wall.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>There&rsquo;s the ideal world where we indeed influence products, but sometimes, we also hit a brick wall, a dead end, we&rsquo;re not at the helm, and our feedback is not taken into account. Our companies can be big, work in silos, and it&rsquo;s sometimes a struggle to find the right people who are able to listen to us, and are able to get change enacted. Be resilient, let it not affect you personally, but return to the charge if you really think it&rsquo;s important for your community!</p>
<p><figure>
  <a href="#img-ada68f15f833cffb5caa977dfc0e2d56">
    <img src="/img/developer-advocacy-feedback-loop/06&#43;-&#43;the&#43;advocacy&#43;loop.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-ada68f15f833cffb5caa977dfc0e2d56">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/developer-advocacy-feedback-loop/06&#43;-&#43;the&#43;advocacy&#43;loop.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Remember: We&rsquo;re in it together! It&rsquo;s a team&rsquo;s effort. Let&rsquo;s make our users happy! And how to make them happy? By making great products, with great user and developer experience. By showing empathy toward our users, wear their shoes, listen to their feedback, and let that feedback be heard up above, to improve our products, by advocating for our users. That&rsquo;s where the feedback loop closes. Thanks for your attention.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Running Micronaut serverlessly on Google Cloud Platform</title><link>https://glaforge.dev/posts/2020/08/04/running-micronaut-serverlessly-on-google-cloud-platform/</link><pubDate>Tue, 04 Aug 2020 18:49:49 +0100</pubDate><guid>https://glaforge.dev/posts/2020/08/04/running-micronaut-serverlessly-on-google-cloud-platform/</guid><description>&lt;p>Last week, I had the pleasure of presenting &lt;a href="https://micronaut.io/">Micronaut&lt;/a> in action on Google Cloud Platform, via a &lt;a href="https://objectcomputing.com/products/micronaut/resources/serverless-micronaut-on-google-cloud">webinar&lt;/a> organized by OCI. Particularly, I focused on the serverless compute options available: &lt;a href="https://cloud.google.com/functions">Cloud Functions&lt;/a>, &lt;a href="http://cloud.google.com/appengine">App Engine&lt;/a>, and &lt;a href="https://cloud.google.com/run">Cloud Run&lt;/a>.&lt;/p>
&lt;p>Here are the slides I presented. However, the real meat is in the demos which are not displayed on this deck! So let&amp;rsquo;s have a closer look at them, until the video is published online.&lt;/p>
&lt;script async class="speakerdeck-embed" data-id="9da7fe86bd4047508effb1ae34af5ed0" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js">&lt;/script>
&lt;p>On Google Cloud Platform, you have three solutions when you want to deploy your code in a serverless fashion (ie. hassle-free infrastructure, automatic scaling, pays-as-you-go): &lt;/p></description><content:encoded>
<![CDATA[<p>Last week, I had the pleasure of presenting <a href="https://micronaut.io/">Micronaut</a> in action on Google Cloud Platform, via a <a href="https://objectcomputing.com/products/micronaut/resources/serverless-micronaut-on-google-cloud">webinar</a> organized by OCI. Particularly, I focused on the serverless compute options available: <a href="https://cloud.google.com/functions">Cloud Functions</a>, <a href="http://cloud.google.com/appengine">App Engine</a>, and <a href="https://cloud.google.com/run">Cloud Run</a>.</p>
<p>Here are the slides I presented. However, the real meat is in the demos which are not displayed on this deck! So let&rsquo;s have a closer look at them, until the video is published online.</p>
<script async class="speakerdeck-embed" data-id="9da7fe86bd4047508effb1ae34af5ed0" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>On Google Cloud Platform, you have three solutions when you want to deploy your code in a serverless fashion (ie. hassle-free infrastructure, automatic scaling, pays-as-you-go): </p>
<ul>
<li>For event-oriented logic that reacts to cloud events (a new file in cloud storage, a change in a database document, a Pub/Sub message) you can go with a function. </li>
<li>For a web frontend, a REST API, a mobile API backend, also for serving static assets for single-page apps, App Engine is going to do wonders. </li>
<li>But you can also decide to containerize your applications and run them as containers on Cloud Run, for all kinds of needs.</li>
</ul>
<p>Both Cloud Functions and App Engine provide a Java 11 runtime (the latest LTS version of Java at the time of writing), but with Cloud Run, in a container, you can of course package whichever Java runtime environment that you want.</p>
<p>And the good news is that you can run Micronaut easily on all those three environments!</p>
<h2 id="micronaut-on-cloud-functions">Micronaut on Cloud Functions</h2>
<h3 id="http-functions">HTTP functions</h3>
<p>Of those three solutions, Cloud Functions is the one that received a special treatment, as the Micronaut team worked on a <a href="https://micronaut-projects.github.io/micronaut-gcp/2.0.x/guide/#cloudFunction">dedicated integration</a> with the <a href="https://github.com/GoogleCloudPlatform/functions-framework-java">Functions Framework API</a> for Java. Micronaut supports both types of functions: HTTP and background functions.</p>
<p>For HTTP functions, you can use a plain Micronaut controller. Your usual controllers can be turned into an HTTP function.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">package</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.example</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.micronaut.http.annotation.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#555;font-weight:bold">@Controller</span>(<span style="color:#4070a0">&#34;/hello&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">HelloController</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Get</span>(uri<span style="color:#666">=</span><span style="color:#4070a0">&#34;/&#34;</span>,<span style="color:#bbb"> </span>produces<span style="color:#666">=</span><span style="color:#4070a0">&#34;text/plain&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">index</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Micronaut on Cloud Functions&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Micronaut Launch tool even allows you to create a dedicated scaffolded project with the right configuration (ie. the right Micronaut integration JAR, the Gradle configuration, including for running functions locally on your machine.) Pick the application type in the Launch configuration, and add the <code>google-cloud-function</code> module.</p>
<p>In build.gradle, Launch will add the Functions Frameworks&rsquo; invoker dependency, which allows you to run your functions locally on your machine (it&rsquo;s also the framework that is used in the cloud to invoke your functions, ie. the same portable and open source code):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>invoker<span style="color:#666">(</span><span style="color:#4070a0">&#34;com.google.cloud.functions.invoker:java-function-invoker:1.0.0-beta1&#34;</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>It adds the Java API of the Functions Framework, as <code>compileOnly</code> as it&rsquo;s provided by the platform when running in the cloud:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>compileOnly<span style="color:#666">(</span><span style="color:#4070a0">&#34;com.google.cloud.functions:functions-framework-api&#34;</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>And Micronaut&rsquo;s own GCP Functions integration dependency:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>implementation<span style="color:#666">(</span><span style="color:#4070a0">&#34;io.micronaut.gcp:micronaut-gcp-function-http&#34;</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>And there&rsquo;s also a new task called <code>runFunction</code>, which allows you to run your function locally:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>./gradlew runFunction
</span></span></code></pre></div><p>If you decide to use Maven, the same dependencies are applied to your project, but there&rsquo;s a dedicated Maven plugin that is provided to run functions locally.</p>
<pre tabindex="0"><code>./mvnw function:run
</code></pre><p>Then to deploy your HTTP function, you can learn more about the topic in the <a href="https://cloud.google.com/functions/docs/deploying">documentation</a>. If you deploy with the gcloud command-line SDK, you will deploy with a command similar to the following one (depending on the region, or size of the instance you want to use):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud functions deploy hello <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --region europe-west1 <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --trigger-http --allow-unauthenticated <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --runtime java11 --memory 512MB <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --entry-point io.micronaut.gcp.function.http.HttpFunction
</span></span></code></pre></div><p>Note that Cloud Functions can build your functions from sources when you deploy, or it can deploy a pre-build shadowed JAR (as configured by Launch.)</p>
<h3 id="background-functions">Background functions</h3>
<p>For background functions, in Launch, select the Micronaut serverless function type. Launch will create a class implementing the BackgroundFunction interface from the Function Frameworks APIs. But it will extend the <code>GoogleFunctionInitializer</code> class from Micronaut&rsquo;s function integration, which takes care of all the usual wiring (like dependency injection). This function by default receives a Pub/Sub message, but there are other types of events that you can receive, like when a new file is uploaded in cloud storage, a new or changed document in the Firestore nosql document database, etc.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">package</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.example</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.cloud.functions.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.micronaut.gcp.function.GoogleFunctionInitializer</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">javax.inject.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">PubSubFunction</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">extends</span><span style="color:#bbb"> </span>GoogleFunctionInitializer<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">implements</span><span style="color:#bbb"> </span>BackgroundFunction<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Inject</span><span style="color:#bbb"> </span>LoggingService<span style="color:#bbb"> </span>loggingService;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">accept</span>(PubSubMessage<span style="color:#bbb"> </span>pubsubMsg,<span style="color:#bbb"> </span>Context<span style="color:#bbb"> </span>context)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>textMessage<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>String(Base64.<span style="color:#4070a0">getDecoder</span>().<span style="color:#4070a0">decode</span>(pubsubMsg.<span style="color:#4070a0">data</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>loggingService.<span style="color:#4070a0">logMessage</span>(textMessage);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">PubSubMessage</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>data;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Map<span style="color:#bbb"> </span>attributes;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>messageId;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>publishTime;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#555;font-weight:bold">@Singleton</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">LoggingService</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">logMessage</span>(String<span style="color:#bbb"> </span>txtMessage)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(txtMessage);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>When deploying, you&rsquo;ll define a different trigger, for example here, it&rsquo;s a Pub/Sub message, so you&rsquo;ll use a <code>--trigger-topic TOPIC_NAME</code> flag to tell the platform you want to receive messages on that topic.</p>
<p>For deployment, the gcloud command would look as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud functions deploy pubsubFn <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --region europe-west1 <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --trigger-topic TOPIC_NAME <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --runtime java11 --memory 512MB <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --entry-point com.example.PubSubFunction
</span></span></code></pre></div><h2 id="micronaut-on-app-engine">Micronaut on App Engine</h2>
<p>Micronaut deploys fine as well on App Engine. I <a href="https://glaforge.dev/posts/2019/07/04/getting-started-with-micronaut-on-google-app-engine-java-11/">wrote about it</a> in the past already. If you&rsquo;re using Micronaut Launch, just select the Application type. App Engine allows you to deploy the standalone runnable JARs generated by the configured shadow JAR plugin. But if you want to easily stage your application deliverable, to run the application locally, to deploy, you can also use the Gradle App Engine plugin.</p>
<p>For that purpose, you should add the following build script section in <code>build.gradle</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>buildscript <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    repositories <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        mavenCentral<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    dependencies <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        classpath <span style="color:#4070a0">&#39;com.google.cloud.tools:appengine-gradle-plugin:2.3.0&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>And then apply the plugin with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>apply <span style="color:#002070;font-weight:bold">plugin:</span> <span style="color:#4070a0">&#39;com.google.cloud.tools.appengine&#39;</span>
</span></span></code></pre></div><p>Before packaging the application, there&rsquo;s one extra step you need to go through, which is to add the special App Engine configuration file: app.yaml. You only need to add one line, unless you want to further configure the instance types, specify some JVM flags, point at static assets, etc. But otherwise, you only need this line in <code>src/main/appengine/app.yaml</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">runtime</span>:<span style="color:#bbb"> </span>java11<span style="color:#bbb">
</span></span></span></code></pre></div><p>Then, stage your application deliverable with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>./gradlew appengineStage
</span></span></code></pre></div><p>Cd in the directory, and you can deploy with the plugin or with the gcloud SDK:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007020">cd</span> build/staged-app/
</span></span><span style="display:flex;"><span>gcloud app deploy
</span></span></code></pre></div><p>During the demonstration, I showed a controller that was accessing some data from the <a href="https://cloud.google.com/firestore/">Cloud Firestore</a> nosql database, listing some pet names:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">package</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.example</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.api.core.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.cloud.firestore.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.cloud.firestore.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.micronaut.http.annotation.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#555;font-weight:bold">@Controller</span>(<span style="color:#4070a0">&#34;/&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">WelcomeController</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Get</span>(uri<span style="color:#666">=</span><span style="color:#4070a0">&#34;/&#34;</span>,<span style="color:#bbb"> </span>produces<span style="color:#666">=</span><span style="color:#4070a0">&#34;text/html&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">index</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Hello Google Cloud!&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Get</span>(uri<span style="color:#666">=</span><span style="color:#4070a0">&#34;/pets&#34;</span>,<span style="color:#bbb"> </span>produces<span style="color:#666">=</span><span style="color:#4070a0">&#34;application/json&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">pets</span>()<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">throws</span><span style="color:#bbb"> </span>Exception<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>StringBuilder<span style="color:#bbb"> </span>petNames<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>StringBuilder().<span style="color:#4070a0">append</span>(<span style="color:#4070a0">&#34;[&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>FirestoreOptions<span style="color:#bbb"> </span>opts<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>FirestoreOptions.<span style="color:#4070a0">getDefaultInstance</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Firestore<span style="color:#bbb"> </span>db<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>opts.<span style="color:#4070a0">getService</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>ApiFuture<span style="color:#bbb"> </span>query<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>db.<span style="color:#4070a0">collection</span>(<span style="color:#4070a0">&#34;pets&#34;</span>).<span style="color:#4070a0">get</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>QuerySnapshot<span style="color:#bbb"> </span>querySnapshot<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>query.<span style="color:#4070a0">get</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>List<span style="color:#bbb"> </span>documents<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>querySnapshot.<span style="color:#4070a0">getDocuments</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">for</span><span style="color:#bbb"> </span>(QueryDocumentSnapshot<span style="color:#bbb"> </span>document<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>documents)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>petNames.<span style="color:#4070a0">append</span>(<span style="color:#4070a0">&#34;\&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">append</span>(document.<span style="color:#4070a0">getString</span>(<span style="color:#4070a0">&#34;name&#34;</span>))<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>.<span style="color:#4070a0">append</span>(<span style="color:#4070a0">&#34;\&#34;, &#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>petNames.<span style="color:#4070a0">append</span>(<span style="color:#4070a0">&#34;]&#34;</span>).<span style="color:#4070a0">toString</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="micronaut-on-cloud-run">Micronaut on Cloud Run</h2>
<h3 id="building-a-micronaut-container-image-with-jib">Building a Micronaut container image with Jib</h3>
<p>In a previous article, I talked about how to try <a href="https://glaforge.dev/posts/2020/03/24/start-the-fun-with-java-14-and-micronaut-inside-serverless-containers-on-cloud-run/">Micronaut with Java 14 on Google Cloud</a>. I was explaining how to craft your own <code>Dockerfile</code>, instead of the one generated then by default by Micronaut Launch (now, it is using <code>openjdk:14-alpine</code>). But instead of fiddling with Docker, in my demos, I thought it was cleaner to use Jib. <a href="https://github.com/GoogleContainerTools/jib">Jib</a> is a tool to create cleanly layered container images for your Java applications, without requiring a Docker daemon. There are plugins available for Gradle and Maven, I used the Gradle one by configuring my <code>build.gradle</code> with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>plugins <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">...</span>
</span></span><span style="display:flex;"><span>    id <span style="color:#4070a0">&#34;com.google.cloud.tools.jib&#34;</span> version <span style="color:#4070a0">&#34;2.4.0&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>And by configuring the <code>jib</code> task with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>jib <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    to <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        image <span style="color:#666">=</span> <span style="color:#4070a0">&#34;gcr.io/serverless-micronaut/micronaut-news&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    from <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        image <span style="color:#666">=</span> <span style="color:#4070a0">&#34;openjdk:14-alpine&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>The from/image line defines the base image to use, and the <code>to</code>/<code>image</code> points at the location in Google Cloud Container Registry where the image will be built, and we can then point Cloud Run at this image for deployment:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud config <span style="color:#007020">set</span> run/region europe-west1
</span></span><span style="display:flex;"><span>gcloud config <span style="color:#007020">set</span> run/platform managed
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>./gradlew jib
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>gcloud run deploy news --image gcr.io/serverless-micronaut/micronaut-news --allow-unauthenticated
</span></span></code></pre></div><h2 id="bonus-points-server-sent-events">Bonus points: Server-Sent Events</h2>
<p>In the demo, I showed the usage of <a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events">Server-Sent Events</a>. Neither Cloud Functions nor App Engine support any kind of streaming, as there&rsquo;s a global frontend server in the Google Cloud infrastructure that buffers requests and responses. But Cloud Run supports streaming (HTTP/2 streaming, gRPC streaming, server-sent events, and WebSocket streaming).</p>
<p>So that was a great excuse to play with Micronaut&rsquo;s SSE support. I went with a slightly modified example from the documentation, to emit a few string messages a second apart:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">package</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.example</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.micronaut.http.MediaType</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.micronaut.http.annotation.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.micronaut.http.sse.Event</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.micronaut.scheduling.TaskExecutors</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.micronaut.scheduling.annotation.ExecuteOn</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.reactivex.Flowable</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">org.reactivestreams.Publisher</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#555;font-weight:bold">@Controller</span>(<span style="color:#4070a0">&#34;/news&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">NewsController</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@ExecuteOn</span>(TaskExecutors.<span style="color:#4070a0">IO</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Get</span>(produces<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>MediaType.<span style="color:#4070a0">TEXT_EVENT_STREAM</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>Publisher<span style="color:#666">&gt;</span><span style="color:#bbb"> </span><span style="color:#06287e">index</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#666">[]</span><span style="color:#bbb"> </span>ids<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>String<span style="color:#666">[]</span><span style="color:#bbb"> </span>{<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;1&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;2&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;3&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;4&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;5&#34;</span><span style="color:#bbb"> </span>};<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Flowable.<span style="color:#4070a0">generate</span>(()<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>0,<span style="color:#bbb"> </span>(i,<span style="color:#bbb"> </span>emitter)<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(i<span style="color:#bbb"> </span><span style="color:#666">&lt;</span><span style="color:#bbb"> </span>ids.<span style="color:#4070a0">length</span>)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>emitter.<span style="color:#4070a0">onNext</span>(<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                    </span>Event.<span style="color:#4070a0">of</span>(<span style="color:#4070a0">&#34;Event #&#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>i)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>{<span style="color:#bbb"> </span>Thread.<span style="color:#4070a0">sleep</span>(1000);<span style="color:#bbb"> </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">catch</span><span style="color:#bbb"> </span>(Throwable<span style="color:#bbb"> </span>t)<span style="color:#bbb"> </span>{}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>}<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>emitter.<span style="color:#4070a0">onComplete</span>();<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#666">++</span>i;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>});<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Then I accessed the /news controller and was happy to see that the response was not buffered and that the events were showing up every second.</p>
<p>Apart from getting on board of this alpha feature of Cloud Run (via the form mentioned to get my GCP project whitelisted), I didn&rsquo;t have to do anything special to my Micronaut setup from the previous section. No further configuration required, it just worked out of the box.</p>
<h2 id="summary">Summary</h2>
<p>The great benefit to using Micronaut on Google Cloud Platform&rsquo;s serverless solutions is that thanks to Micronaut&rsquo;s ahead-of-time compilation techniques, it starts and runs super fast, and consumes much less memory than other Java frameworks. Further down the road, you can also take advantage of GraalVM for even faster startup and lower memory usage. Although my examples were in Java, you can also use Kotlin or Groovy if you prefer.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Video: Getting started with Java on Google Cloud Functions</title><link>https://glaforge.dev/talks/2020/07/28/video-getting-started-with-java-on-google-cloud-functions/</link><pubDate>Tue, 28 Jul 2020 13:01:28 +0100</pubDate><guid>https://glaforge.dev/talks/2020/07/28/video-getting-started-with-java-on-google-cloud-functions/</guid><description>&lt;p>For the 24 hours of talks by Google Cloud DevRel, I recorded my talk about the new Java 11 runtime for Google Cloud Functions.
I wrote about this runtime in this &lt;a href="http://glaforge.appspot.com/article/deploying-serverless-functions-in-groovy-on-the-new-java-11-runtime-for-google-cloud-functions">article&lt;/a> 
showing for example how to run &lt;a href="https://groovy-lang.org/">Apache Groovy&lt;/a> functions, and I also wrote about it on the 
&lt;a href="https://cloud.google.com/blog/products/application-development/introducing-java-11-on-google-cloud-functions">GCP blog&lt;/a> 
and &lt;a href="https://developers.googleblog.com/2020/05/java-11-for-cloud-functions.html">Google Developers blog&lt;/a> as well.&lt;/p>
&lt;p>In this video, I&amp;rsquo;m giving a quick explanations on the serverless approach, the various serverless options provided by Google Cloud,
and then I dive into the various shapes Java functions can take (HTTP and background functions),
the interfaces you have to implement when authoring a function.
And I also do various demonstrations, deploying Java functions, Groovy functions, or Micronaut functions!&lt;/p></description><content:encoded>
<![CDATA[<p>For the 24 hours of talks by Google Cloud DevRel, I recorded my talk about the new Java 11 runtime for Google Cloud Functions.
I wrote about this runtime in this <a href="http://glaforge.appspot.com/article/deploying-serverless-functions-in-groovy-on-the-new-java-11-runtime-for-google-cloud-functions">article</a> 
showing for example how to run <a href="https://groovy-lang.org/">Apache Groovy</a> functions, and I also wrote about it on the 
<a href="https://cloud.google.com/blog/products/application-development/introducing-java-11-on-google-cloud-functions">GCP blog</a> 
and <a href="https://developers.googleblog.com/2020/05/java-11-for-cloud-functions.html">Google Developers blog</a> as well.</p>
<p>In this video, I&rsquo;m giving a quick explanations on the serverless approach, the various serverless options provided by Google Cloud,
and then I dive into the various shapes Java functions can take (HTTP and background functions),
the interfaces you have to implement when authoring a function.
And I also do various demonstrations, deploying Java functions, Groovy functions, or Micronaut functions!</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/d7eYFI-jbcc?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Introducing Java 11 on Google Cloud Functions</title><link>https://glaforge.dev/posts/2020/05/27/introducing-java-11-on-google-cloud-functions/</link><pubDate>Wed, 27 May 2020 11:47:45 +0100</pubDate><guid>https://glaforge.dev/posts/2020/05/27/introducing-java-11-on-google-cloud-functions/</guid><description>&lt;p>The Java programming language recently turned 25 years old, and it&amp;rsquo;s still one of the top-used languages powering today&amp;rsquo;s enterprise application customers. On Google Cloud, you can already run serverless Java microservices in App Engine and Cloud Run. Today we&amp;rsquo;re bringing Java 11 to Google Cloud Functions, an event-driven serverless compute platform that lets you run locally or in the cloud without having to provision servers. That means you can now write Cloud Functions using your favorite JVM languages (&lt;a href="https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/functions/helloworld/helloworld">Java&lt;/a>, &lt;a href="https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/functions/helloworld/kotlin-helloworld">Kotlin&lt;/a>, &lt;a href="https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/functions/helloworld/groovy-helloworld">Groovy&lt;/a>, &lt;a href="https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/functions/helloworld/scala-helloworld">Scala&lt;/a>, etc) with our &lt;a href="https://github.com/GoogleCloudPlatform/functions-framework-java">Functions Framework for Java&lt;/a>, and also with &lt;a href="https://cloud.spring.io/spring-cloud-static/spring-cloud-function/3.0.6.RELEASE/reference/html/gcp.html">Spring Cloud Functions&lt;/a> and &lt;a href="https://micronaut-projects.github.io/micronaut-gcp/2.0.x/guide/#simpleFunctions">Micronaut&lt;/a>!&lt;/p></description><content:encoded>
<![CDATA[<p>The Java programming language recently turned 25 years old, and it&rsquo;s still one of the top-used languages powering today&rsquo;s enterprise application customers. On Google Cloud, you can already run serverless Java microservices in App Engine and Cloud Run. Today we&rsquo;re bringing Java 11 to Google Cloud Functions, an event-driven serverless compute platform that lets you run locally or in the cloud without having to provision servers. That means you can now write Cloud Functions using your favorite JVM languages (<a href="https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/functions/helloworld/helloworld">Java</a>, <a href="https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/functions/helloworld/kotlin-helloworld">Kotlin</a>, <a href="https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/functions/helloworld/groovy-helloworld">Groovy</a>, <a href="https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/functions/helloworld/scala-helloworld">Scala</a>, etc) with our <a href="https://github.com/GoogleCloudPlatform/functions-framework-java">Functions Framework for Java</a>, and also with <a href="https://cloud.spring.io/spring-cloud-static/spring-cloud-function/3.0.6.RELEASE/reference/html/gcp.html">Spring Cloud Functions</a> and <a href="https://micronaut-projects.github.io/micronaut-gcp/2.0.x/guide/#simpleFunctions">Micronaut</a>!</p>
<p>With Cloud Functions for Java 11, now in beta, you can use Java to build business-critical applications and integration layers, and deploy the function in a fully managed environment, complete with access to resources in a private VPC network. Java functions will scale automatically based on your load. You can write <a href="https://cloud.google.com/functions/docs/writing/http">HTTP functions</a> to respond to HTTP events, and <a href="https://cloud.google.com/functions/docs/writing/background">background functions</a> to process events sourced from various cloud and GCP services, such as Pub/Sub, Cloud Storage, Firestore, and more.</p>
<p><figure>
  <a href="#img-d036b25bbb3757b4928c8057371f001b">
    <img src="/img/j11gcf/Cloud_Functions__Product_Strategy-01.jpg"
      alt="/img/j11gcf/Cloud_Functions__Product_Strategy-01.jpg"
       />
  </a>
  <figcaption>/img/j11gcf/Cloud_Functions__Product_Strategy-01.jpg</figcaption>
</figure>
<div class="lightbox" id="img-d036b25bbb3757b4928c8057371f001b">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/j11gcf/Cloud_Functions__Product_Strategy-01.jpg"
    alt="/img/j11gcf/Cloud_Functions__Product_Strategy-01.jpg"
     />
  <div class="lightbox-caption">/img/j11gcf/Cloud_Functions__Product_Strategy-01.jpg</div>
</div>
</p>
<p>Functions are a great fit for serverless application backends for integrating with third-party services and APIs, or for mobile or IoT backends. You can also use functions for real-time data processing systems, like processing files as they are uploaded to Cloud Storage, or to handle real-time streams of events from Pub/Sub. Last but not least, functions can serve intelligent applications like virtual assistants and chat bots, or video, image and sentiment analysis.</p>
<h2 id="cloud-functions-for-java-11-example">Cloud Functions for Java 11 example</h2>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/UsYRKkibLPI?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>You can develop functions using the <a href="https://github.com/GoogleCloudPlatform/functions-framework-java/">Functions Framework for Java</a>, an open source functions-as-a-service framework for writing portable Java functions. You can develop and run your functions locally, deploy them to Cloud Functions, or to another Java environment.</p>
<p>An HTTP function simply implements the <a href="https://javadoc.io/static/com.google.cloud.functions/functions-framework-api/1.0.1/com/google/cloud/functions/HttpFunction.html"><code>HttpFunction</code></a> interface:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">HelloWorld</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">implements</span><span style="color:#bbb"> </span>HttpFunction<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">service</span>(HttpRequest<span style="color:#bbb"> </span>request,<span style="color:#bbb"> </span>HttpResponse<span style="color:#bbb"> </span>response)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">throws</span><span style="color:#bbb"> </span>IOException<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>writer<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>response.<span style="color:#4070a0">getWriter</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>writer.<span style="color:#4070a0">write</span>(<span style="color:#4070a0">&#34;Hello world!&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Add the Functions Framework API dependency to the Maven pom.xml:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>com.google.cloud.functions<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>functions-framework-api<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>1.0.1<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;scope&gt;</span>provided<span style="color:#062873;font-weight:bold">&lt;/scope&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div><p>Then add the the <a href="https://github.com/GoogleCloudPlatform/functions-framework-java#running-a-function-with-the-maven-plugin">Function Maven plugin</a> so you can run the function locally:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;plugin&gt;</span>
</span></span><span style="display:flex;"><span> <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>com.google.cloud.functions<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span> <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>function-maven-plugin<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span> <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>0.9.2<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span> <span style="color:#062873;font-weight:bold">&lt;configuration&gt;</span>
</span></span><span style="display:flex;"><span> <span style="color:#062873;font-weight:bold">&lt;functionTarget&gt;</span>function.HelloWorld<span style="color:#062873;font-weight:bold">&lt;/functionTarget&gt;</span>
</span></span><span style="display:flex;"><span> <span style="color:#062873;font-weight:bold">&lt;/configuration&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/plugin&gt;</span>
</span></span></code></pre></div><p>Run the function locally:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mvn <span style="color:#007020;font-weight:bold">function</span>:run
</span></span></code></pre></div><p>You can also use your IDE to launch this Maven target in Debugger mode to debug the function locally.</p>
<p><figure>
  <a href="#img-ef0c6676edfcadf6a0281ffd9957c032">
    <img src="/img/j11gcf/2_Cloud_Functions_for_Java_11.max-900x900.png"
      alt="/img/j11gcf/2_Cloud_Functions_for_Java_11.max-900x900.png"
       />
  </a>
  <figcaption>/img/j11gcf/2_Cloud_Functions_for_Java_11.max-900x900.png</figcaption>
</figure>
<div class="lightbox" id="img-ef0c6676edfcadf6a0281ffd9957c032">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/j11gcf/2_Cloud_Functions_for_Java_11.max-900x900.png"
    alt="/img/j11gcf/2_Cloud_Functions_for_Java_11.max-900x900.png"
     />
  <div class="lightbox-caption">/img/j11gcf/2_Cloud_Functions_for_Java_11.max-900x900.png</div>
</div>
</p>
<p>To deploy the function, you can use the gcloud command line:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud beta functions deploy helloworld-function <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span> --entry-point <span style="color:#007020;font-weight:bold">function</span>.HelloWorld --runtime java11 --trigger-http
</span></span></code></pre></div><p>Alternatively, you can also deploy with the Function Maven plugin:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mvn <span style="color:#007020;font-weight:bold">function</span>:deploy -Dfunction.deploy.name<span style="color:#666">=</span>helloworld-function
</span></span></code></pre></div><p>You can find the <a href="https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/functions/helloworld/helloworld">full example on GitHub</a>. In addition to running this function in the fully managed Cloud Functions environment, you can also <a href="https://github.com/GoogleCloudPlatform/functions-framework-java#running-the-functions-framework-directly">bring the Functions Framework runtime</a> with you to other environments, such as Cloud Run, Google Kubernetes Engine, or a virtual machine. In addition, Java 8 users can now take advantage of Java 11 features. The majority of the use cases of the Java 8 programming model are supported in Java 11.</p>
<h2 id="third-party-framework-support">Third-party framework support</h2>
<p>In addition to our Functions Framework for Java, both the <a href="https://micronaut-projects.github.io/micronaut-gcp/2.0.x/guide/#simpleFunctions">Micronaut </a>framework and the <a href="https://spring.io/projects/spring-cloud-function">Spring Cloud Function</a> project now have out-of-the-box support for Google Cloud Functions. You can create both an HTTP function and background function using the respective framework&rsquo;s programming model, including capabilities like dependency injection.</p>
<h3 id="micronaut">Micronaut</h3>
<p>The Micronaut team implemented dedicated support for the Cloud Functions Java 11 runtime. Instead of implementing Functions Framework&rsquo;s HttpFunction interface directly, you can use Micronaut&rsquo;s programming model, such that a Helloworld HTTP Function can simply be a <a href="https://docs.micronaut.io/2.0.0.M2/guide/index.html#creatingServer">Micronaut controller</a>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Controller</span>(<span style="color:#4070a0">&#34;/hello&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">HelloController</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Get</span>(<span style="color:#4070a0">&#34;/{name}&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span><span style="color:#06287e">greet</span>(String<span style="color:#bbb"> </span>name)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Hello &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>name;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>You can find a <a href="https://github.com/micronaut-projects/micronaut-gcp/tree/master/examples/hello-world-cloud-function">full example of Micronaut with Cloud Functions</a> and its <a href="https://micronaut-projects.github.io/micronaut-gcp/snapshot/guide/#httpFunctions">documentation</a> on GitHub.</p>
<h3 id="spring-cloud-functions">Spring Cloud Functions</h3>
<p>The Google Cloud Java Frameworks team worked with the Spring team to bring <a href="https://spring.io/projects/spring-cloud-gcp">Spring Cloud GCP</a> project to help Spring Boot users easily leverage Google Cloud services. More recently, the team worked with the Spring Cloud Function team to bring you <a href="https://cloud.spring.io/spring-cloud-static/spring-cloud-function/3.0.7.RELEASE/reference/html/gcp.html">Spring Cloud Function GCP Adapter</a>. A function can just be a vanilla Java function, so you can run a Spring Cloud Function application on Cloud Functions without having to modify your code to run on Google Cloud.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Bean</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>Function<span style="color:#666">&lt;</span>String,<span style="color:#bbb"> </span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span><span style="color:#06287e">uppercase</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>value<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>value.<span style="color:#4070a0">toUpperCase</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>You can find a full example of a <a href="https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-samples/function-sample-gcp-http">Spring Cloud Function with Cloud Functions</a> on GitHub.</p>
<h2 id="jvm-languages">JVM Languages</h2>
<p>In addition to using the <a href="https://advancedweb.hu/new-language-features-since-java-8-to-14/">latest Java 11 language features</a> with Cloud Functions, you can also use your favorite JVM languages, such as Kotlin, Groovy, and Scala, and more. For example, here&rsquo;s a function written with Kotlin:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">HelloWorld</span> : HttpFunction {
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">fun</span> <span style="color:#06287e">helloWorld</span>(req: HttpRequest, res: HttpResponse) {
</span></span><span style="display:flex;"><span>        with(res.writer) {
</span></span><span style="display:flex;"><span>            write(<span style="color:#4070a0">&#34;Hello Kotlin World!&#34;</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Here&rsquo;s the same function with Groovy:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">HelloWorld</span> <span style="color:#007020;font-weight:bold">implements</span> HttpFunction <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">void</span> <span style="color:#06287e">service</span><span style="color:#666">(</span>HttpRequest req<span style="color:#666">,</span> HttpResponse res<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        res<span style="color:#666">.</span><span style="color:#4070a0">writer</span><span style="color:#666">.</span><span style="color:#4070a0">write</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;Hello Groovy World!&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>You can take a <a href="http://glaforge.appspot.com/article/deploying-serverless-functions-in-groovy-on-the-new-java-11-runtime-for-google-cloud-functions">deeper dive into a Groovy example</a>, and otherwise, find all the examples on GitHub (<a href="https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/functions/helloworld/kotlin-helloworld">Kotlin</a>, <a href="https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/functions/helloworld/groovy-helloworld">Groovy</a>, <a href="https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/functions/helloworld/scala-helloworld">Scala</a>).</p>
<h2 id="try-cloud-functions-for-java-11-today">Try Cloud Functions for Java 11 today</h2>
<p>Cloud Functions for Java 11 is now in beta, so you can try it today with your favorite JVM language and frameworks. Read the <a href="https://cloud.google.com/functions/docs/quickstart-java">Quick Start guide</a>, learn how to <a href="https://cloud.google.com/functions/docs/first-java">write your first functions</a>, and try it out with a Google Cloud Platform <a href="https://cloud.google.com/free">free trial</a>. If you want to dive a little bit deeper into the technical aspects, you can also read this <a href="https://developers.googleblog.com/2020/05/java-11-for-cloud-functions.html">article on Google Developers blog</a>. If you&rsquo;re interested in the <a href="https://github.com/GoogleCloudPlatform/functions-framework-java">open-source Functions Framework</a> for Java, please don&rsquo;t hesitate to have a look at the project and potentially even contribute to it. We&rsquo;re looking forward to seeing all the Java the functions you write! </p>
<hr />
<p><em>Special thanks to Googlers Éamonn McManus, Magda Zakrzewska‎, Sławek Walkowski, Ludovic Champenois, Katie McCormick, Grant Timmerman, Ace Nassri, Averi Kitsch, Les Vogel, Kurtis Van Gent, Ronald Laeremans, Mike Eltsufin, Dmitry Solomakha, Daniel Zou, Jason Polites, Stewart Reichling, Michael Skura, Karol Farbiś, and Vinod Ramachandran. We also want to thank <a href="https://micronaut.io/">Micronaut</a> and <a href="https://spring.io/projects/spring-cloud-function">Spring Cloud Function</a> teams for working on the Cloud Functions support!</em></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Sip a Cup of Java 11 for Your Cloud Functions</title><link>https://glaforge.dev/posts/2020/05/26/sip-a-cup-of-java-11-for-your-cloud-functions/</link><pubDate>Tue, 26 May 2020 17:17:34 +0100</pubDate><guid>https://glaforge.dev/posts/2020/05/26/sip-a-cup-of-java-11-for-your-cloud-functions/</guid><description>&lt;p>With the &lt;a href="https://cloud.google.com/blog/products/application-development/introducing-java-11-on-google-cloud-functions">beta of the new Java 11 runtime&lt;/a> for Google &lt;a href="https://cloud.google.com/functions">Cloud Functions&lt;/a>, Java developers can now write their functions using the Java programming language (a language often used in enterprises) in addition to Node.js, Go, or Python. Cloud Functions allow you to run bits of code locally or in the cloud, without provisioning or managing servers: Deploy your code, and let the platform handle scaling up and down for you. Just focus on your code: handle incoming HTTP requests or respond to some cloud events, like messages coming from Cloud Pub/Sub or new files uploaded in Cloud Storage buckets.&lt;/p></description><content:encoded>
<![CDATA[<p>With the <a href="https://cloud.google.com/blog/products/application-development/introducing-java-11-on-google-cloud-functions">beta of the new Java 11 runtime</a> for Google <a href="https://cloud.google.com/functions">Cloud Functions</a>, Java developers can now write their functions using the Java programming language (a language often used in enterprises) in addition to Node.js, Go, or Python. Cloud Functions allow you to run bits of code locally or in the cloud, without provisioning or managing servers: Deploy your code, and let the platform handle scaling up and down for you. Just focus on your code: handle incoming HTTP requests or respond to some cloud events, like messages coming from Cloud Pub/Sub or new files uploaded in Cloud Storage buckets.</p>
<p>In this article, let&rsquo;s focus on what functions look like, how you can write portable functions, how to run and debug them locally or deploy them in the cloud or on-premises, thanks to the <a href="https://github.com/GoogleCloudPlatform/functions-framework-java">Functions Framework</a>, an open source library that runs your functions. But you will also learn about third-party frameworks that you might be familiar with, that also let you create functions using common programming paradigms.</p>
<h2 id="the-shape-of-your-functions">The shape of your functions</h2>
<p>There are two types of functions: <a href="https://cloud.google.com/functions/docs/writing/http">HTTP functions</a>, and <a href="https://cloud.google.com/functions/docs/writing/background">background functions</a>. HTTP functions respond to incoming HTTP requests, whereas background functions react to cloud-related events.</p>
<p>The Java Functions Framework provides an <a href="https://javadoc.io/doc/com.google.cloud.functions/functions-framework-api/latest/index.html">API</a> that you can use to author your functions, as well as an invoker which can be called to run your functions locally on your machine, or anywhere with a Java 11 environment.</p>
<p>To get started with this API, you will need to add a dependency in your build files. If you use Maven, add the following dependency tag in <code>pom.xml</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>com.google.cloud.functions<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>functions-framework-api<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>1.0.1<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;scope&gt;</span>provided<span style="color:#062873;font-weight:bold">&lt;/scope&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div><p>If you are using Gradle, add this dependency declaration in <code>build.gradle</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>compileOnly<span style="color:#666">(</span><span style="color:#4070a0">&#34;com.google.cloud.functions:functions-framework-api&#34;</span><span style="color:#666">)</span>
</span></span></code></pre></div><h3 id="responding-to-http-requests">Responding to HTTP requests</h3>
<p>A Java function that receives an incoming HTTP request implements the <code>[HttpFunction](https://javadoc.io/doc/com.google.cloud.functions/functions-framework-api/latest/com/google/cloud/functions/HttpFunction.html)</code> interface:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.cloud.functions.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.io.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Example</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">implements</span><span style="color:#bbb"> </span>HttpFunction<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">service</span>(HttpRequest<span style="color:#bbb"> </span>request,<span style="color:#bbb"> </span>HttpResponse<span style="color:#bbb"> </span>response)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">throws</span><span style="color:#bbb"> </span>IOException<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>writer<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>response.<span style="color:#4070a0">getWriter</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>writer.<span style="color:#4070a0">write</span>(<span style="color:#4070a0">&#34;Hello developers!&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>The <code>service()</code> method provides an <code>[HttpRequest](https://javadoc.io/doc/com.google.cloud.functions/functions-framework-api/latest/com/google/cloud/functions/HttpRequest.html)</code> and an <code>[HttpResponse](https://javadoc.io/doc/com.google.cloud.functions/functions-framework-api/latest/com/google/cloud/functions/HttpResponse.html)</code> object. From the request, you can get information about the HTTP headers, the payload body, or the request parameters. It&rsquo;s also possible to handle multipart requests. With the response, you can set a status code or headers, define a body payload and a content-type.</p>
<h2 id="responding-to-cloud-events">Responding to cloud events</h2>
<p>Background functions respond to events coming from the cloud, like new <a href="https://cloud.google.com/pubsub">Pub/Sub</a> messages, <a href="https://cloud.google.com/storage">Cloud Storage</a> file updates, or new or updated data in <a href="https://cloud.google.com/firestore">Cloud Firestore</a>. There are actually two ways to implement such functions, either by dealing with the JSON payloads representing those events, or by taking advantage of object marshalling thanks to the <a href="https://github.com/google/gson">Gson</a> library, which takes care of the parsing transparently for the developer.</p>
<p>With a <a href="https://javadoc.io/doc/com.google.cloud.functions/functions-framework-api/latest/com/google/cloud/functions/RawBackgroundFunction.html">RawBackgroundFunction</a>, the responsibility is on you to handle the incoming cloud event JSON-encoded payload. You receive a JSON string, so you are free to parse it however you like, with your JSON parser of your choice:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.cloud.functions.Context</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.cloud.functions.RawBackgroundFunction</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">RawFunction</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">implements</span><span style="color:#bbb"> </span>RawBackgroundFunction<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">accept</span>(String<span style="color:#bbb"> </span>json,<span style="color:#bbb"> </span>Context<span style="color:#bbb"> </span>context)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>...<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>But you also have the option to write a <a href="https://javadoc.io/doc/com.google.cloud.functions/functions-framework-api/latest/com/google/cloud/functions/BackgroundFunction.html">BackgroundFunction</a> which uses Gson for unmarshalling a JSON representation into a Java class (a POJO, Plain-Old-Java-Object) representing that payload. To that end, you have to provide the POJO as a generic argument:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.cloud.functions.Context</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.google.cloud.functions.BackgroundFunction</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">PubSubFunction</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">implements</span><span style="color:#bbb"> </span>BackgroundFunction<span style="color:#666">&lt;</span>PubSubMsg<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Override</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">accept</span>(PubSubMsg<span style="color:#bbb"> </span>msg,<span style="color:#bbb"> </span>Context<span style="color:#bbb"> </span>context)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;Received message ID: &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>msg.<span style="color:#4070a0">messageId</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">PubSubMsg</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>data;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>Map<span style="color:#666">&lt;</span>String,<span style="color:#bbb"> </span>String<span style="color:#666">&gt;</span><span style="color:#bbb"> </span>attributes;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>messageId;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>String<span style="color:#bbb"> </span>publishTime;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>The <a href="https://javadoc.io/doc/com.google.cloud.functions/functions-framework-api/latest/com/google/cloud/functions/Context.html">Context</a> parameter contains various metadata fields like timestamps, the type of events, and other attributes.</p>
<p>Which type of background function should you use? It depends on the control you need to have on the incoming payload, or if the Gson unmarshalling doesn&rsquo;t fully fit your needs. But having the unmarshalling covered by the framework definitely streamlines the writing of your function.</p>
<h2 id="running-your-function-locally">Running your function locally</h2>
<p>Coding is always great, but seeing your code actually running is even more rewarding. The Functions Framework comes with the API we used above, but also with an invoker tool that you can use to run functions locally. For improving developer productivity, having a direct and local feedback loop on your own computer makes it much more comfortable than deploying in the cloud for each change you make to your code.</p>
<h3 id="with-maven">With Maven</h3>
<p>If you&rsquo;re building your functions with Maven, you can install the Function Maven plugin in your <code>pom.xml</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;plugin&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>com.google.cloud.functions<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>function-maven-plugin<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>0.9.2<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;configuration&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;functionTarget&gt;</span>com.example.Example<span style="color:#062873;font-weight:bold">&lt;/functionTarget&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;/configuration&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/plugin&gt;</span>
</span></span></code></pre></div><p>On the command-line, you can then run:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ mvn <span style="color:#007020;font-weight:bold">function</span>:run
</span></span></code></pre></div><p>You can pass extra parameters like <code>--target</code> to define a different function to run (in case your project contains several functions), <code>--port</code> to specify the port to listen to, or <code>--classpath</code> to explicitly set the classpath needed by the function to run. These are the parameters of the underlying <code>[Invoker](https://github.com/GoogleCloudPlatform/functions-framework-java/blob/master/invoker/core/src/main/java/com/google/cloud/functions/invoker/runner/Invoker.java)</code> class. However, to set these parameters via the Maven plugin, you&rsquo;ll have to pass properties with <code>-Drun.functionTarget=com.example.Example</code> and <code>-Drun.port</code>.</p>
<h3 id="with-gradle">With Gradle</h3>
<p>With Gradle, there is no dedicated plugin, but it&rsquo;s easy to configure <code>build.gradle</code> to let you run functions.</p>
<p>First, define a dedicated configuration for the invoker:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>configurations <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    invoker
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>In the dependencies, add the Invoker library:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>dependencies <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    invoker <span style="color:#4070a0">&#39;com.google.cloud.functions.invoker:java-function-invoker:1.0.0-beta1&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>And then, create a new task to run the Invoker:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>tasks<span style="color:#666">.</span><span style="color:#4070a0">register</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;runFunction&#34;</span><span style="color:#666">,</span> JavaExec<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    main <span style="color:#666">=</span> <span style="color:#4070a0">&#39;com.google.cloud.functions.invoker.runner.Invoker&#39;</span>
</span></span><span style="display:flex;"><span>    classpath<span style="color:#666">(</span>configurations<span style="color:#666">.</span><span style="color:#4070a0">invoker</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    inputs<span style="color:#666">.</span><span style="color:#4070a0">files</span><span style="color:#666">(</span>configurations<span style="color:#666">.</span><span style="color:#4070a0">runtimeClasspath</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>                 sourceSets<span style="color:#666">.</span><span style="color:#4070a0">main</span><span style="color:#666">.</span><span style="color:#4070a0">output</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    args<span style="color:#666">(</span><span style="color:#4070a0">&#39;--target&#39;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>         project<span style="color:#666">.</span><span style="color:#4070a0">findProperty</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;runFunction.target&#39;</span><span style="color:#666">)</span> <span style="color:#666">?:</span>
</span></span><span style="display:flex;"><span>         <span style="color:#4070a0">&#39;com.example.Example&#39;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>         <span style="color:#4070a0">&#39;--port&#39;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>         project<span style="color:#666">.</span><span style="color:#4070a0">findProperty</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;runFunction.port&#39;</span><span style="color:#666">)</span> <span style="color:#666">?:</span> <span style="color:#40a070">8080</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    doFirst <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        args<span style="color:#666">(</span><span style="color:#4070a0">&#39;--classpath&#39;</span><span style="color:#666">,</span> files<span style="color:#666">(</span>configurations<span style="color:#666">.</span><span style="color:#4070a0">runtimeClasspath</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>                                  sourceSets<span style="color:#666">.</span><span style="color:#4070a0">main</span><span style="color:#666">.</span><span style="color:#4070a0">output</span><span style="color:#666">).</span><span style="color:#4070a0">asPath</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>By default, the above launches the function <code>com.example.Example</code> on port 8080, but you can override those on the command-line, when running gradle or the gradle wrapper:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ gradle runFunction -PrunFunction.target<span style="color:#666">=</span>com.example.HelloWorld<span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>                     -PrunFunction.port<span style="color:#666">=</span><span style="color:#40a070">8080</span>
</span></span></code></pre></div><h3 id="running-elsewhere-making-your-functions-portable">Running elsewhere, making your functions portable</h3>
<p>What&rsquo;s interesting about the <a href="https://github.com/GoogleCloudPlatform/functions-framework-java">Functions Framework</a> is that you are not tied to the Cloud Functions platform for deploying your functions. As long as, in your target environment, you can run your functions with the Invoker class, you can run your functions on <a href="https://cloud.google.com/run">Cloud Run</a>, on <a href="https://cloud.google.com/kubernetes-engine">Google Kubernetes Engine</a>, on <a href="https://cloud.google.com/knative/">Knative</a> environments, on other clouds when you can run Java, or more generally on any servers on-premises. It makes your functions highly portable between environments. But let&rsquo;s have a closer look at deployment now.</p>
<h2 id="deploying-your-functions">Deploying your functions</h2>
<p>You can <a href="https://github.com/GoogleCloudPlatform/functions-framework-java/blob/master/invoker/function-maven-plugin/src/main/java/com/google/cloud/functions/plugin/DeployFunction.java">deploy</a> functions with the Maven plugin as well, with various parameters to tweak for defining regions, memory size, etc. But here, we&rsquo;ll focus on using the <a href="https://cloud.google.com/sdk">cloud SDK</a>, with its <code>gcloud</code> command-line, to deploy our functions.</p>
<p>For example, to deploy an HTTP function, you would type:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ gcloud functions deploy exampleFn<span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --region europe-west1<span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --trigger-http<span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --allow-unauthenticated<span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --runtime java11<span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --entry-point com.example.Example<span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --memory 512MB
</span></span></code></pre></div><p>For a background function that would be notified of new messages on a Pub/Sub topic, you would launch:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ gcloud functions deploy exampleFn<span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --region europe-west1<span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --trigger-topic msg-topic<span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --runtime java11<span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --entry-point com.example.PubSubFunction<span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --memory 512MB
</span></span></code></pre></div><p>Note that deployments come in two flavors as well, although the above commands are the same: functions are deployed from source with a <code>pom.xml</code> and built in Google Cloud, but when using a build tool other than Maven, you can also use the same command to deploy a pre-compiled JAR that contains your function implementation. Of course, you&rsquo;ll have to create that JAR first.</p>
<h2 id="what-about-other-languages-and-frameworks">What about other languages and frameworks?</h2>
<p>So far, we looked at Java and the plain <a href="https://github.com/GoogleCloudPlatform/functions-framework-java">Functions Framework</a>, but you can definitely use alternative JVM languages such as <a href="http://groovy-lang.org/">Apache Groovy</a>, <a href="https://kotlinlang.org/">Kotlin</a>, or <a href="https://www.scala-lang.org/">Scala</a>, and third-party frameworks that integrate with Cloud Functions like <a href="https://micronaut.io/">Micronaut</a> and <a href="https://spring.io/projects/spring-boot">Spring Boot</a>!</p>
<h3 id="pretty-groovy-functions">Pretty Groovy functions</h3>
<p>Without covering all those combinations, let&rsquo;s have a look at two examples. What would an HTTP function look like in Groovy?</p>
<p>The first step will be to add Apache Groovy as a dependency in your <code>pom.xml</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>org.codehaus.groovy<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>groovy-all<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>3.0.4<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;type&gt;</span>pom<span style="color:#062873;font-weight:bold">&lt;/type&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div><p>You will also need the GMaven compiler plugin to compile the Groovy code:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;plugin&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>org.codehaus.gmavenplus<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>gmavenplus-plugin<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>1.9.0<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;executions&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;execution&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;goals&gt;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#062873;font-weight:bold">&lt;goal&gt;</span>addSources<span style="color:#062873;font-weight:bold">&lt;/goal&gt;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#062873;font-weight:bold">&lt;goal&gt;</span>addTestSources<span style="color:#062873;font-weight:bold">&lt;/goal&gt;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#062873;font-weight:bold">&lt;goal&gt;</span>compile<span style="color:#062873;font-weight:bold">&lt;/goal&gt;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#062873;font-weight:bold">&lt;goal&gt;</span>compileTests<span style="color:#062873;font-weight:bold">&lt;/goal&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;/goals&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;/execution&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;/executions&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/plugin&gt;</span>
</span></span></code></pre></div><p>When writing the function code, just use Groovy instead of Java:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">com.google.cloud.functions.*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">HelloWorldFunction</span> <span style="color:#007020;font-weight:bold">implements</span> HttpFunction <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">void</span> <span style="color:#06287e">service</span><span style="color:#666">(</span>HttpRequest request<span style="color:#666">,</span> HttpResponse response<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        response<span style="color:#666">.</span><span style="color:#4070a0">writer</span><span style="color:#666">.</span><span style="color:#4070a0">write</span> <span style="color:#4070a0">&#34;Hello Groovy World!&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>The same explanations regarding running your function locally or deploying it still applies: the Java platform is pretty open to alternative languages too! And the Cloud Functions builder will happily build your Groovy code in the cloud, since Maven lets you compile this code thanks to the Groovy library.</p>
<h3 id="micronaut-functions">Micronaut functions</h3>
<p>Third-party frameworks also offer a dedicated Cloud Functions integration. Let&rsquo;s have a look at Micronaut.</p>
<p><a href="https://micronaut.io/">Micronaut</a> is a <em>&ldquo;modern, JVM-based, full-stack framework for building modular, easily testable microservice and serverless applications&rdquo;</em>, as explained on its website. It supports the notion of serverless functions, web apps and microservices, and has a dedicated integration for Google Cloud Functions.</p>
<p>In addition to being a very efficient framework with super fast startup times (which is important, to avoid long cold starts on serverless services), what&rsquo;s interesting about using Micronaut is that you can use Micronaut&rsquo;s own programming model, including Dependency Injection, annotation-driven bean declaration, etc.</p>
<p>For HTTP functions, you can use the framework&rsquo;s own <code>@Controller</code> / <code>@Get</code> annotations, instead of the Functions Framework&rsquo;s own interfaces. So for example, a Micronaut HTTP function would look like:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.micronaut.http.annotation.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#555;font-weight:bold">@Controller</span>(<span style="color:#4070a0">&#34;/hello&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">HelloController</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Get</span>(uri<span style="color:#666">=</span><span style="color:#4070a0">&#34;/&#34;</span>,<span style="color:#bbb"> </span>produces<span style="color:#666">=</span><span style="color:#4070a0">&#34;text/plain&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">index</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Example Response&#34;</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>This is the standard way in Micronaut to define a Web microservice, but it transparently builds upon the Functions Framework to run this service as a Cloud Function. Furthermore, this programming model offered by Micronaut is portable across other environments, since Micronaut runs in many different contexts.</p>
<p>Last but not least, if you are using the <a href="https://micronaut.io/launch/">Micronaut Launch</a> project (hosted on <a href="https://cloud.google.com/run">Cloud Run</a>) which allows you to scaffold new projects easily (from the command-line or from a nice UI), you can opt for adding the <code>google-cloud-function</code> support module, and even choose your favorite language, build tool, or testing framework:</p>
<p><a href="https://3.bp.blogspot.com/-dgW0l2rewn0/Xs1EprFntNI/AAAAAAAAI3Q/XlDOiwKN_98CGp8qGSRJkTv_ETjqynbjgCLcBGAsYHQ/s1600/micronautlaunch.png"><figure>
  <a href="#img-05e491d60c8302b0446a9de48b68b6d6">
    <img src="https://3.bp.blogspot.com/-dgW0l2rewn0/Xs1EprFntNI/AAAAAAAAI3Q/XlDOiwKN_98CGp8qGSRJkTv_ETjqynbjgCLcBGAsYHQ/s1600/micronautlaunch.png"
      alt="Micronaut Launch"
       />
  </a>
  <figcaption>Micronaut Launch</figcaption>
</figure>
<div class="lightbox" id="img-05e491d60c8302b0446a9de48b68b6d6">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://3.bp.blogspot.com/-dgW0l2rewn0/Xs1EprFntNI/AAAAAAAAI3Q/XlDOiwKN_98CGp8qGSRJkTv_ETjqynbjgCLcBGAsYHQ/s1600/micronautlaunch.png"
    alt="Micronaut Launch"
     />
  <div class="lightbox-caption">Micronaut Launch</div>
</div>
</a></p>
<p>Be sure to check out the documentation for the <a href="https://micronaut-projects.github.io/micronaut-gcp/2.0.0.M3/guide/index.html#cloudFunction">Micronaut Cloud Functions support</a>, and <a href="https://cloud.spring.io/spring-cloud-static/spring-cloud-function/3.0.6.RELEASE/reference/html/gcp.html">Spring Cloud Function support</a>.</p>
<h2 id="whats-next">What&rsquo;s next?</h2>
<p>Now it&rsquo;s your turn to try Cloud Functions for Java 11 today, with your favorite JVM language or third-party frameworks. Read the <a href="https://cloud.google.com/functions/docs/quickstart-java">getting started guide</a>, and try this for free with Google Cloud Platform <a href="https://cloud.google.com/free">free trial</a>. Explore Cloud Functions&rsquo; <a href="https://cloud.google.com/functions">features and use cases</a>, take a look at the <a href="https://cloud.google.com/functions/docs/quickstarts">quickstarts</a>, perhaps even contribute to the <a href="https://github.com/GoogleCloudPlatform/functions-framework-java">open source Functions Framework</a>. And we&rsquo;re looking forward to seeing what functions you&rsquo;re going to build on this platform!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Deploying serverless functions in Groovy on the new Java 11 runtime for Google Cloud Functions</title><link>https://glaforge.dev/posts/2020/05/26/deploying-serverless-functions-in-groovy-on-the-new-java-11-runtime-for-google-cloud-functions/</link><pubDate>Tue, 26 May 2020 13:05:07 +0100</pubDate><guid>https://glaforge.dev/posts/2020/05/26/deploying-serverless-functions-in-groovy-on-the-new-java-11-runtime-for-google-cloud-functions/</guid><description>&lt;p>Java celebrates its 25th anniversary! 
Earlier this year, the Apache Groovy team released the big 
&lt;a href="https://groovy-lang.org/releasenotes/groovy-3.0.html">3.0 version&lt;/a> of the programming language. 
&lt;a href="https://github.com/groovy/GMavenPlus/releases/tag/1.9.0">GMavenPlus&lt;/a> was published in version 1.9
(the Maven plugin for compiling Groovy code) which works with Java 14.
And today, Google Cloud opens up the beta of the 
&lt;a href="https://cloud.google.com/blog/products/application-development/introducing-java-11-on-google-cloud-functions">Java 11 runtime for Cloud Functions&lt;/a>.
What about combining them all?&lt;/p>
&lt;p>I&amp;rsquo;ve been working for a bit on the Java 11 runtime for Google Cloud Functions
(that&amp;rsquo;s the Function-as-a-Service platform of Google Cloud, pay-as-you-go, hassle-free / transparent scaling),
and in this article, I&amp;rsquo;d like to highlight that you can also write and deploy functions with alternative JVM languages 
like &lt;a href="http://groovy-lang.org/">Apache Groovy&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>Java celebrates its 25th anniversary! 
Earlier this year, the Apache Groovy team released the big 
<a href="https://groovy-lang.org/releasenotes/groovy-3.0.html">3.0 version</a> of the programming language. 
<a href="https://github.com/groovy/GMavenPlus/releases/tag/1.9.0">GMavenPlus</a> was published in version 1.9
(the Maven plugin for compiling Groovy code) which works with Java 14.
And today, Google Cloud opens up the beta of the 
<a href="https://cloud.google.com/blog/products/application-development/introducing-java-11-on-google-cloud-functions">Java 11 runtime for Cloud Functions</a>.
What about combining them all?</p>
<p>I&rsquo;ve been working for a bit on the Java 11 runtime for Google Cloud Functions
(that&rsquo;s the Function-as-a-Service platform of Google Cloud, pay-as-you-go, hassle-free / transparent scaling),
and in this article, I&rsquo;d like to highlight that you can also write and deploy functions with alternative JVM languages 
like <a href="http://groovy-lang.org/">Apache Groovy</a>.</p>
<p>So today, you&rsquo;re going to:</p>
<ul>
<li>Write a simple Groovy 3.0 function,</li>
<li>Compile it with Maven 3.6 and the GMavenPlus 1.9 plugin, </li>
<li>Deploy and run the function on the Cloud Functions Java 11 runtime!</li>
</ul>
<p>Note: If you want to try this at (work from?) home, you will need an account on Google Cloud,
you can easily create a <a href="https://cloud.google.com/free">free account</a> 
and benefit from $300 of cloud credits to get started (including also free quotas for many products).
You will also need to create a billing account, but for the purpose of this tutorial,
you should be within the free quota (so your credit card shouldn&rsquo;t be billed).
Then, head over to the <a href="https://console.cloud.google.com/">console.cloud.google.com</a> cloud console to create a new project.
And then navigate to the Cloud Functions section to enable the service for your project.</p>
<p>Let&rsquo;s get started! So what do we need? A pom.xml file, and a Groovy class!</p>
<p>Let&rsquo;s start with the <code>pom.xml</code> file, and what you should add to your build file.
First of all, since I&rsquo;m using Groovy as my function implementation language, I&rsquo;m going to use GMavenPlus for compilation.
So in the build/plugins section, I configure the plugin as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;plugin&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>org.codehaus.gmavenplus<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>gmavenplus-plugin<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>1.9.0<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;executions&gt;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&lt;execution&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;id&gt;</span>groovy-compile<span style="color:#062873;font-weight:bold">&lt;/id&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;phase&gt;</span>process-resources<span style="color:#062873;font-weight:bold">&lt;/phase&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;goals&gt;</span>
</span></span><span style="display:flex;"><span>              <span style="color:#062873;font-weight:bold">&lt;goal&gt;</span>addSources<span style="color:#062873;font-weight:bold">&lt;/goal&gt;</span>
</span></span><span style="display:flex;"><span>              <span style="color:#062873;font-weight:bold">&lt;goal&gt;</span>compile<span style="color:#062873;font-weight:bold">&lt;/goal&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;/goals&gt;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&lt;/execution&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;/executions&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;dependencies&gt;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>org.codehaus.groovy<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>groovy-all<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>3.0.4<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;scope&gt;</span>runtime<span style="color:#062873;font-weight:bold">&lt;/scope&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;type&gt;</span>pom<span style="color:#062873;font-weight:bold">&lt;/type&gt;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;/dependencies&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;/plugin&gt;</span>
</span></span></code></pre></div><p>That way, when I do an <code>mvn compile</code>, my Groovy sources are compiled as part of the compilation lifecycle of Maven.</p>
<p>But I&rsquo;m adding a second plugin, the Functions Framework plugin!
That&rsquo;s a Maven plugin to run functions locally on your machine,
before deploying into the cloud, so that you can have a local developer experience that&rsquo;s easy and fast.
The <a href="https://github.com/GoogleCloudPlatform/functions-framework-java">Functions Framework</a> is actually an open source project on Github.
It&rsquo;s a lightweight API to write your functions with, and it&rsquo;s also a function runner / invoker.
What&rsquo;s interesting is that it also means that you are not locked in the Cloud Functions platform,
but you can run your function locally or anywhere else where you can run a JAR file on a JVM! Great portability!</p>
<p>So let&rsquo;s configure the Functions Framework Maven plugin:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;plugin&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>com.google.cloud.functions<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>function-maven-plugin<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>0.9.2<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;configuration&gt;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&lt;functionTarget&gt;</span>mypackage.HelloWorldFunction<span style="color:#062873;font-weight:bold">&lt;/functionTarget&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;/configuration&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;/plugin&gt;</span>
</span></span></code></pre></div><p>I specify a configuration flag to point at the function I want to run.
But we&rsquo;ll come back in a moment on how to run this function locally. We need to write it first!</p>
<p>We need two more things in our <code>pom.xml</code>, a dependency on Groovy, but also on the Functions Framework Java API.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>com.google.cloud.functions<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>functions-framework-api<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>1.0.1<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;scope&gt;</span>provided<span style="color:#062873;font-weight:bold">&lt;/scope&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>org.codehaus.groovy<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>groovy-all<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>3.0.4<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;type&gt;</span>pom<span style="color:#062873;font-weight:bold">&lt;/type&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div><p>So you&rsquo;re all set for the build, let&rsquo;s now create our function in <code>src/main/groovy/mypackage/HelloWorldFunction.groovy</code>.</p>
<p>There are two flavors of functions: <a href="https://cloud.google.com/functions/docs/writing/http">HTTP functions</a> and 
<a href="https://cloud.google.com/functions/docs/writing/background">background functions</a>.
The latter react to cloud events like a new file stored in Cloud Storage, a new data update in the Firestore database, etc.
Whereas the former directly exposes a URL that can be invoked via an HTTP call.
That&rsquo;s the one I want to create to write a symbolic <code>&quot;Hello Groovy World&quot;</code> message in your browser window.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">package</span> mypackage
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">com.google.cloud.functions.*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">HelloWorldFunction</span> <span style="color:#007020;font-weight:bold">implements</span> HttpFunction <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">void</span> <span style="color:#06287e">service</span><span style="color:#666">(</span>HttpRequest request<span style="color:#666">,</span> HttpResponse response<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        response<span style="color:#666">.</span><span style="color:#4070a0">writer</span><span style="color:#666">.</span><span style="color:#4070a0">write</span> <span style="color:#4070a0">&#34;Hello Groovy World!&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Yes, that&rsquo;s all there is to it!
You implement a Functions Framework interface, and its <code>service()</code> method.
You have a <code>request</code> / <code>response</code> mode (a <code>request</code> and a <code>response</code> parameters are passed to your method).
You can access the writer to write back to the browser or client that invoked the function.</p>
<p>Now it&rsquo;s time to run the function locally to see if it&rsquo;s working. Just type the following command in your terminal:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mvn <span style="color:#007020;font-weight:bold">function</span>:run
</span></span></code></pre></div><p>After a moment, and some build logs further, you should see something like:</p>
<pre tabindex="0"><code>INFO: Serving function...
INFO: Function: mypackage.HelloWorldFunction
INFO: URL: http://localhost:8080/
</code></pre><p>With your browser (or curl), you can browse this local URL, and you will see the hello world message appearing. Yay!</p>
<p>With the Maven plugin, you can also deploy, but you can use the <a href="https://cloud.google.com/sdk">gcloud</a> 
command-line tool to deploy the function:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud functions deploy helloFunction<span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --region europe-west1<span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --trigger-http --allow-unauthenticated<span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --runtime java11<span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --entry-point mypackage.HelloWorldFunction<span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --memory 512MB
</span></span></code></pre></div><p>After a little moment, the function is deployed, and you&rsquo;ll notice you&rsquo;ll have a URL created for your function looking something like this:</p>
<pre tabindex="0"><code>https://europe-west1-myprojectname.cloudfunctions.net/helloFunction
</code></pre><p>The very same function now runs in the cloud!
A pretty Groovy function! This function is portable:
you can invoke it with the Functions Framework invoker, anywhere you can run a JVM.</p>
<p>Going further, I encourage you to have a look at the <a href="https://github.com/GoogleCloudPlatform/functions-framework-java">Functions Framework documentation on
Github</a> to learn more about it.
Here you deployed the function source and the pom.xml file, as the function will be built directly in the cloud.
But it&rsquo;s also possible to compile and create a JAR locally and deploy that instead.
That&rsquo;s interesting for example if you want to use another build tool, like Gradle. And this will be the purpose of another upcoming article!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Video: the Pic-a-Daily serverless workshop</title><link>https://glaforge.dev/talks/2020/05/18/video-the-pic-a-daily-serverless-workshop/</link><pubDate>Mon, 18 May 2020 14:44:33 +0100</pubDate><guid>https://glaforge.dev/talks/2020/05/18/video-the-pic-a-daily-serverless-workshop/</guid><description>&lt;p>With my partner in crime, &lt;a href="https://twitter.com/meteatamel/status/1262316915642576896">Mete Atamel&lt;/a>,
we ran two editions of our &amp;ldquo;Pic-a-Daily&amp;rdquo; serverless workshop. It&amp;rsquo;s an online, hands-on, workshop,
where developers get their hands on the the &lt;a href="https://cloud.google.com/serverless">serverless products&lt;/a> 
provided by &lt;a href="https://cloud.google.com/">Google Cloud Platform&lt;/a>:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://cloud.google.com/functions">Cloud Functions&lt;/a> &amp;mdash; to develop and run functions, small units of logic glue, to react to events of your cloud projects and services&lt;/li>
&lt;li>&lt;a href="https://cloud.google.com/appengine">App Engine&lt;/a> &amp;mdash; to deploy web apps, for web frontends, or API backends&lt;/li>
&lt;li>&lt;a href="https://cloud.google.com/run">Cloud Run&lt;/a> &amp;mdash; to deploy and scale containerised services&lt;/li>
&lt;/ul>
&lt;p>&lt;a href="https://codelabs.developers.google.com/serverless-workshop/">&lt;figure>
&lt;a href="#img-49f695532cf3b3364eef7700f42716d6">
&lt;img src="https://glaforge.dev/img/pic-a-daily-workshop/picadaily.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-49f695532cf3b3364eef7700f42716d6">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/pic-a-daily-workshop/picadaily.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/a>&lt;/p></description><content:encoded>
<![CDATA[<p>With my partner in crime, <a href="https://twitter.com/meteatamel/status/1262316915642576896">Mete Atamel</a>,
we ran two editions of our &ldquo;Pic-a-Daily&rdquo; serverless workshop. It&rsquo;s an online, hands-on, workshop,
where developers get their hands on the the <a href="https://cloud.google.com/serverless">serverless products</a> 
provided by <a href="https://cloud.google.com/">Google Cloud Platform</a>:</p>
<ul>
<li><a href="https://cloud.google.com/functions">Cloud Functions</a> &mdash; to develop and run functions, small units of logic glue, to react to events of your cloud projects and services</li>
<li><a href="https://cloud.google.com/appengine">App Engine</a> &mdash; to deploy web apps, for web frontends, or API backends</li>
<li><a href="https://cloud.google.com/run">Cloud Run</a> &mdash; to deploy and scale containerised services</li>
</ul>
<p><a href="https://codelabs.developers.google.com/serverless-workshop/"><figure>
  <a href="#img-49f695532cf3b3364eef7700f42716d6">
    <img src="/img/pic-a-daily-workshop/picadaily.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-49f695532cf3b3364eef7700f42716d6">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/pic-a-daily-workshop/picadaily.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</a></p>
<p>The theme of the workshop is to build a simple photosharing application (hence the play on words, with a picture a day) with those serverless products, but along the way, developers also get to use other services like:</p>
<ul>
<li><a href="https://cloud.google.com/pubsub/">Pub/Sub</a> &mdash; as a messaging fabric to let events flow between your services</li>
<li><a href="https://cloud.google.com/firestore">Firestore</a> &mdash; for storing picture metadata in the scalable document database</li>
<li><a href="https://cloud.google.com/storage">Cloud Storage</a> &mdash; to store the image blobs</li>
<li><a href="https://cloud.google.com/scheduler">Cloud Scheduler</a> &mdash; to run a services on a schedule (ie. cron as a service)</li>
<li><a href="https://cloud.google.com/vision/">Cloud Vision API</a> &mdash; a machine learning API to make sense of what&rsquo;s in your pictures</li>
</ul>
<p><a href="https://codelabs.developers.google.com/serverless-workshop/"><figure>
  <a href="#img-cbae5a5a09b54a4d934b58cb299274e9">
    <img src="/img/pic-a-daily-workshop/diagram.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-cbae5a5a09b54a4d934b58cb299274e9">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/pic-a-daily-workshop/diagram.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</a></p>
<p>The workshop is freely accessible on our codelabs platform: <a href="https://codelabs.developers.google.com/serverless-workshop/">&ldquo;Pic-a-Daily&rdquo; serverless workshop</a>. So you can follow this hands-on workshop on your own, at your own pace. There are 4 codelabs:</p>
<ul>
<li>
<p>The <a href="https://codelabs.developers.google.com/codelabs/cloud-picadaily-lab1/index.html?index=..%2F..serverless-workshop#13">first one</a> lets you build a function that responds to events as new pictures are uploaded into Cloud Storage, invoking the Vision API to understand what is in the picture, and storing some picture metadata information in Firestore.</p>
</li>
<li>
<p>The <a href="https://codelabs.developers.google.com/codelabs/cloud-picadaily-lab2/index.html?index=..%2F..serverless-workshop#0">second lab</a> will use a Cloud Run service which reacts to new files stored in Cloud Storage too, but will create thumbnails of the pictures.</p>
</li>
<li>
<p>A <a href="https://codelabs.developers.google.com/codelabs/cloud-picadaily-lab3/index.html?index=..%2F..serverless-workshop#0">third lab</a> is also taking advantage of Cloud Run to run on a schedule, thanks to Cloud Scheduler. It creates a collage of the most recent pictures.</p>
</li>
<li>
<p>Last but not least, the <a href="https://codelabs.developers.google.com/codelabs/cloud-picadaily-lab4/index.html?index=..%2F..serverless-workshop#0">fourth lab</a> will let you build a web frontend and backend API on Google App Engine.</p>
</li>
</ul>
<p><a href="https://codelabs.developers.google.com/serverless-workshop/"><figure>
  <a href="#img-83c9873aeb95320ff02a4eaa5bc8ab24">
    <img src="/img/pic-a-daily-workshop/codelabs.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-83c9873aeb95320ff02a4eaa5bc8ab24">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/pic-a-daily-workshop/codelabs.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</a></p>
<p>We have a dedicated <a href="https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop">Github repository</a> where you can check-out the code of the various functions, apps and containers, and you can have a look at the <a href="https://speakerdeck.com/meteatamel/pic-a-daily-serverless-workshop">slide deck</a> introducing the workshop and the technologies used.</p>
<p>And now, the videos of the first edition are also available on YouTube!</p>
<p>The first part covers Cloud Functions and Cloud Run with the first two labs:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/wEENQouNsGk?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>The second part covers Cloud Run and App Engine:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/Y9E1fQcPXP0?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Machine learning applied music generation with Magenta</title><link>https://glaforge.dev/posts/2020/05/04/machine-learning-applied-music-generation-with-magenta/</link><pubDate>Mon, 04 May 2020 14:54:06 +0100</pubDate><guid>https://glaforge.dev/posts/2020/05/04/machine-learning-applied-music-generation-with-magenta/</guid><description>&lt;p>I missed this talk from Alexandre Dubreuil, when attending Devoxx Belgium 2019, but I had the chance to watch while doing my elliptical bike run, confined at home. It&amp;rsquo;s about applying Machine Learning to music generation, thanks to the &lt;a href="https://magenta.tensorflow.org/">Magenta project&lt;/a>, which is based on &lt;a href="https://www.tensorflow.org/">Tensorflow&lt;/a>.&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/O4uBa0KMeNY?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video">&lt;/iframe>
&lt;/div>
&lt;p>I like playing music (a bit of piano &amp;amp; guitar) once in a while, so as a geek, I&amp;rsquo;ve also always been interested in computer generated music. And it&amp;rsquo;s hard to generate music that actually sounds pleasant to the ear! Alexandre explains that it&amp;rsquo;s hard to encode the rules a computer could follow to play music, but that machine learning is pretty interesting, as it&amp;rsquo;s able to learn complex functions, thus understanding what does sound good.&lt;/p></description><content:encoded>
<![CDATA[<p>I missed this talk from Alexandre Dubreuil, when attending Devoxx Belgium 2019, but I had the chance to watch while doing my elliptical bike run, confined at home. It&rsquo;s about applying Machine Learning to music generation, thanks to the <a href="https://magenta.tensorflow.org/">Magenta project</a>, which is based on <a href="https://www.tensorflow.org/">Tensorflow</a>.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/O4uBa0KMeNY?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>I like playing music (a bit of piano &amp; guitar) once in a while, so as a geek, I&rsquo;ve also always been interested in computer generated music. And it&rsquo;s hard to generate music that actually sounds pleasant to the ear! Alexandre explains that it&rsquo;s hard to encode the rules a computer could follow to play music, but that machine learning is pretty interesting, as it&rsquo;s able to learn complex functions, thus understanding what does sound good.</p>
<p>He, then, covers the various types of music representations, like MIDI scores which are quite light in terms of data, and audio waves which on the high end of data as there are thousands of data points representing the position on the wave along the time axis. While MIDI represents a note of music, audio waves really represent the sound physically as a wave (of data points).</p>
<p>Note that in the following part of the article, I&rsquo;m not an ML / AI expert, so I&rsquo;m just trying to explain what I actually understood :-)</p>
<p>For MIDI, Recurrent Neural Networks (RNN) make sense, as they work on sequences for the input and output, and also have the ability to remember past information. And that&rsquo;s great as you find recurring patterns in music (series of chords, main song lines, etc.)</p>
<p>RNN tend to forget progressively those past events, so those networks often use Long-Short-Term-Memory to keep some of their memory fresh.</p>
<p>Variational Auto-Encoders are a pair of networks that diminish the dimensions of outputs compared to the quantity in input, but to then re-expand back to the same size of output. So VAEs try to actually generate back something that&rsquo;s close to what was initially given in input, but it events to reproduce similar patterns.</p>
<p>For audio waves, Magenta comes with a Convolutional Neural Network (CNN) called WaveNet, that&rsquo;s used for example for voice generation on devices like Google Home. There are WaveNet Auto-Encoders that also generate audio waves, because it can learn to generate the actual sound of instruments, or create totally new instruments, or mixes of sounds. Alexandre shows some cool demos of weird instruments made of cat sounds and musical instruments.</p>
<p>Magenta comes with various RNNs for drums, melody, polyphony, performance. With auto-encoders for WaveNet and MIDI too. There&rsquo;s also a Generative Adversarial Network (GAN) for audio waves. GANs are often used for generating things like pictures, for example.</p>
<p>The demos in this presentation are quite cool, with creating new instruments (cat + musical instrument), or for generating sequences of notes (drum score, melody score)</p>
<p>Alexandre ends the presentation with pointers to things like data sets of music, as neural networks further need to learn about style, performance, and networks need plenty of time to learn from existing music and instrument sounds, so as to create something nice to hear! He shows briefly some other cool demos using <a href="https://www.tensorflow.org/js">TensorFlow.js</a>, so that it works in the browser and that you can more easily experiment with music generation.</p>
<p>Also, Alexandre wrote the book &ldquo;<a href="https://www.packtpub.com/eu/data/hands-on-music-generation-with-magenta">Hands-On Music Generation with Magenta</a>&rdquo;, so if you want to dive deeper, there&rsquo;s much to read and experiment with!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>HTML semantic tags</title><link>https://glaforge.dev/posts/2020/04/29/html-semantic-tags/</link><pubDate>Wed, 29 Apr 2020 14:56:35 +0100</pubDate><guid>https://glaforge.dev/posts/2020/04/29/html-semantic-tags/</guid><description>&lt;p>We all know about HTML 5, right? Well, I knew about some of the new semantic tags,
like &lt;code>header&lt;/code> / &lt;code>nav&lt;/code> / &lt;code>main&lt;/code> / &lt;code>article&lt;/code> / &lt;code>aside&lt;/code> / &lt;code>footer&lt;/code>,
but I&amp;rsquo;m still falling down to using tons of divs and spans instead. So as I want to refresh that blog at some point,
it was time I revise those semantic tags. Let&amp;rsquo;s take the little time we have during confinement to learn something!&lt;/p></description><content:encoded>
<![CDATA[<p>We all know about HTML 5, right? Well, I knew about some of the new semantic tags,
like <code>header</code> / <code>nav</code> / <code>main</code> / <code>article</code> / <code>aside</code> / <code>footer</code>,
but I&rsquo;m still falling down to using tons of divs and spans instead. So as I want to refresh that blog at some point,
it was time I revise those semantic tags. Let&rsquo;s take the little time we have during confinement to learn something!</p>
<p>There are likely plenty of videos of the topic, but this one was in my top results, so I watched:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/kGW8Al_cga4?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p><a href="https://www.youtube.com/watch?v=kGW8Al_cga4&amp;list=WL&amp;index=3&amp;t=0s">HTML &amp; CSS Crash Course Tutorial #6 - HTML 5 Semantics</a>.
It&rsquo;s part of a series of videos on the topic of HTML &amp; CSS by the <a href="https://www.youtube.com/channel/UCW5YeuERMmlnqo4oq8vwUpg">Net Ninja</a>.
This particular episode was covering the topic of the semantic tags:</p>
<p>So you have a main tag that wraps the meaty content of your page (ie. not stuff like <code>header</code> / <code>footer</code> / <code>navigation</code>).
Inside, you would put articles, that wrap each piece of content (a blog post, a news article, etc).
Sections tend to be for grouping some other information, like a list of resources, some contact info.
Asides can be related content like similar articles, or something somewhat related to your current article
(perhaps a short bio of a character you&rsquo;re mentioning in your article?)
In the header section, you&rsquo;d put the title of your site, the navigation.
The footer will contain your contact info.</p>
<p>Here&rsquo;s a basic structure of how those tags are organised:</p>
<p><figure>
  <a href="#img-457b6a650e4fa19c9b10a81a222ab147">
    <img src="/img/misc-learning/HTML&#43;5&#43;semantic&#43;tag&#43;structure.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-457b6a650e4fa19c9b10a81a222ab147">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc-learning/HTML&#43;5&#43;semantic&#43;tag&#43;structure.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>After an explanation of those tags, the author does a live demo, building up a web page with all those tags.
So it was a good refresher for me to remember how to use those tags, rather than nesting <code>div</code> after <code>div</code>!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Modern web game development</title><link>https://glaforge.dev/posts/2020/04/27/modern-web-game-development/</link><pubDate>Mon, 27 Apr 2020 15:04:09 +0100</pubDate><guid>https://glaforge.dev/posts/2020/04/27/modern-web-game-development/</guid><description>&lt;p>Next in my series of videos while doing sports at home, I watched this video from my colleague &lt;a href="https://twitter.com/tcmg">Tom Greenaway&lt;/a>!
It&amp;rsquo;s about modern web game development, and was recorded last year at Google I/O.&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/aVTYxHL45SA?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video">&lt;/iframe>
&lt;/div>
&lt;p>There are big gaming platforms, like Sony&amp;rsquo;s PlayStation, Microsoft&amp;rsquo;s XBox, Nintendo Switch,
as well as plenty of mobile games on Android and iOS.
But the Web itself, within your browser, is also a great platform for developing and publishing games!
There&amp;rsquo;s all that&amp;rsquo;s needed for good games!&lt;/p></description><content:encoded>
<![CDATA[<p>Next in my series of videos while doing sports at home, I watched this video from my colleague <a href="https://twitter.com/tcmg">Tom Greenaway</a>!
It&rsquo;s about modern web game development, and was recorded last year at Google I/O.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/aVTYxHL45SA?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>There are big gaming platforms, like Sony&rsquo;s PlayStation, Microsoft&rsquo;s XBox, Nintendo Switch,
as well as plenty of mobile games on Android and iOS.
But the Web itself, within your browser, is also a great platform for developing and publishing games!
There&rsquo;s all that&rsquo;s needed for good games!</p>
<p>Tom explains that you need a functioning game (runs well on device, looks good, sounds good).
And today, most of the game engines you can use for developing games actually provide an HTML5 target.
You need users, and you need to have a good monetisation strategy.
The web already provides all the right APIs for nice graphics, sound mixing, etc, and its a very open platform for spreading virally.</p>
<p>It was pretty interesting to hear about one of the key advantages of the web: it&rsquo;s URLs!
You can be pretty creative with URLs. A game can create a URL for a given game session,
for a particular state in a game, for inviting others to join.</p>
<p>In addition to game engines with a web target, Tom mentions also that it&rsquo;s possible to port games from C/C++ for example,
to JavaScript in the browser, with a tool like <a href="https://emscripten.org/">Emscripten</a>.
Even things like OpenGL 3D rendering can be translated into WebGL.
But he also advises to look at WebAssembly, as it&rsquo;s really become the new approach to native performance in the browser.
He mentioned <a href="https://www.construct.net/fr">construct</a>, it&rsquo;s basically the Box2D game engine, but optimised for WebAssembly.</p>
<p>For 3D graphics, for the web, the future lies in <a href="https://gpuweb.github.io/gpuweb/">WebGPU</a>,
which is a more modern take on WebGL and OpenGL. For audio,
there&rsquo;s the <a href="https://www.w3.org/TR/webaudio/">Web Audio</a> APIs
and worklets which allows you to even create effects in JavaScript or WebAssembly.
But there are other useful APIs for game development,
like the <a href="https://www.w3.org/TR/gamepad/">Gamepad</a> API, the <a href="https://www.w3.org/TR/gyroscope/">Gyroscope</a> API, etc.</p>
<p>For getting users, ensure that your game is fun of course, but also make it fast, in particular load fast,
to avoid using users even before you actually got them to load the game!
But you also need to think about this user acquisition loop: make the game load and start fast to enter the action right away,
so you&rsquo;re really pulled in in the game, and that&rsquo;s then a good reason for users to share this new cool games with others.
Of course, being featured on game sites &amp; libraries helps, it gives a big boost,
but it&rsquo;s not necessarily what will make you earn the most in the long run.
Tom also shares various examples of games that were successful and worked well.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Decoding a QR code by hand</title><link>https://glaforge.dev/posts/2020/04/26/decoding-a-qr-code-by-hand/</link><pubDate>Sun, 26 Apr 2020 15:07:49 +0100</pubDate><guid>https://glaforge.dev/posts/2020/04/26/decoding-a-qr-code-by-hand/</guid><description>&lt;p>Doing sport at home on a treadmill or an elliptical bike is pretty boring, when you&amp;rsquo;re confined,
so to make things more interesting, I&amp;rsquo;m watching some videos to learn something new while exercising.
This time, I found this old video about how to decode QR codes&amp;hellip; by hand!
Have you ever thought how these were encoded?&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/KA8hDldvfv0?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video">&lt;/iframe>
&lt;/div>
&lt;p>This video comes from the &lt;a href="https://www.youtube.com/channel/UCnqifAqGaat6blFe8wlllOw">Robomatics&lt;/a> YouTube channel.
You recognise easily QR codes thanks to the 3 big squares with the inner white line.
I knew about the purple dotted lines that was fixed in those patterns.
What I didn&amp;rsquo;t know however was that there&amp;rsquo;s a mask that is applied to the data, to avoid QR codes to potentially look all white or black.
It was interesting to see also how the bytes were encoded: how they follow a path through out the matrix.&lt;/p></description><content:encoded>
<![CDATA[<p>Doing sport at home on a treadmill or an elliptical bike is pretty boring, when you&rsquo;re confined,
so to make things more interesting, I&rsquo;m watching some videos to learn something new while exercising.
This time, I found this old video about how to decode QR codes&hellip; by hand!
Have you ever thought how these were encoded?</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/KA8hDldvfv0?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>This video comes from the <a href="https://www.youtube.com/channel/UCnqifAqGaat6blFe8wlllOw">Robomatics</a> YouTube channel.
You recognise easily QR codes thanks to the 3 big squares with the inner white line.
I knew about the purple dotted lines that was fixed in those patterns.
What I didn&rsquo;t know however was that there&rsquo;s a mask that is applied to the data, to avoid QR codes to potentially look all white or black.
It was interesting to see also how the bytes were encoded: how they follow a path through out the matrix.</p>
<p>However, what this video doesn&rsquo;t cover, for example, is how error correction is working.
You might have some holes or a bad picture of a QR code, but still being able to decode it with some level of loss of data.
So I&rsquo;ll have to learn how that part works some day!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Defence against the Docker arts by Joe Kutner</title><link>https://glaforge.dev/posts/2020/04/24/defence-against-the-docker-arts-by-joe-kutner/</link><pubDate>Fri, 24 Apr 2020 15:34:25 +0100</pubDate><guid>https://glaforge.dev/posts/2020/04/24/defence-against-the-docker-arts-by-joe-kutner/</guid><description>&lt;p>Confined at home, because of the corona-virus pandemic, I&amp;rsquo;m also doing sport at home. I have a small treadmill for light walks (mostly during conf calls!) and also an elliptical bike. I&amp;rsquo;d much rather run outside though, but I have to use what I have, even if I hate that stationary elliptical bike in my basement. It&amp;rsquo;s so boring! So to avoid feeling like wasting my time, I decided to watch videos during my sessions! Not necessarily series on Netflix. No! But interesting technical videos. So today, I&amp;rsquo;d like to share with you a series of posts on those interesting videos I&amp;rsquo;m watching while exercising.&lt;/p></description><content:encoded>
<![CDATA[<p>Confined at home, because of the corona-virus pandemic, I&rsquo;m also doing sport at home. I have a small treadmill for light walks (mostly during conf calls!) and also an elliptical bike. I&rsquo;d much rather run outside though, but I have to use what I have, even if I hate that stationary elliptical bike in my basement. It&rsquo;s so boring! So to avoid feeling like wasting my time, I decided to watch videos during my sessions! Not necessarily series on Netflix. No! But interesting technical videos. So today, I&rsquo;d like to share with you a series of posts on those interesting videos I&rsquo;m watching while exercising.</p>
<p>Today, thanks to the wonderful <a href="https://twitter.com/codefinger">Joe Kutner</a>, from <a href="https://www.heroku.com/">Heroku</a>, I learned about the Defence Against the Docker Arts! It was recorded at Devoxx Belgium 2019.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/ofH9_sE2qy0?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>Joe starts with clearly differentiating Docker and Dockerfiles. Docker is an ecosystem, while Dockerfiles describe docker container images. An important distinction. The first part of the video, shows best practices on how to writer proper Dockerfiles, and references an article on the Docker blog <a href="https://www.docker.com/blog/intro-guide-to-dockerfile-best-practices/">post</a> from last year on that topic: </p>
<ul>
<li>use official base images, rather than reinventing the wheel, as they are usually more up-to-date and secure</li>
<li>remember that images are built in layers, so to speed up your builds, ensure that the base layers are the ones that change less, and keep your source file changes in the last layer as they change the most</li>
<li>join several RUN commands into one by binding them together with ampersands</li>
<li>be explicit about the version of base images you use</li>
<li>try to chose minimal flavors of base images, as they can be pretty big</li>
<li>build from source in a consistent environment, so that developers are on the same page, with the same version of their environment (build tool, runtime versions)</li>
<li>fetch dependencies in a separate step, so dependencies are cached in their own layer</li>
<li>use multi-staged build to remove build dependencies not needed at runtime</li>
</ul>
<p>That&rsquo;s a lot of things to know! Joe then moves on to talk about higher-level approaches, starting with the <a href="https://github.com/moby/buildkit">BuildKit</a> Go library. It&rsquo;s more for platform developers than developers, but it gives you lots of advanced controls on how to build docker images.</p>
<p>Joe introduces <a href="https://github.com/GoogleContainerTools/jib">Jib</a> which is a build plugin (both for Maven and Gradle) that let developers focus on writing and building their apps, but letting that plugin create properly layered docker images for you, using minimal base images. You can even build without a local Docker daemon.</p>
<p>After BuildKit and Jib, Joe talks about the new Cloud Native <a href="https://buildpacks.io/">BuildPacks</a>, a tool that builds OCI images from source, cleverly. There are buildpacks for plenty of platforms and runtimes, not just Java. Those new cloud native buildpacks build upon years of experience from Heroku and CloudFoundry, on the first version of the buildpack concept. Joe says that buildpacks are reusable, fast, modular and safe, and goes on to show the power of this approach that allowed Heroku, for instance, to safely and rapidly upgrade Heartbleed affected images by replacing the underlying OS with a patched / upgraded version, thanks to image rebasing.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Start the fun with Java 14 and Micronaut inside serverless containers on Cloud Run</title><link>https://glaforge.dev/posts/2020/03/24/start-the-fun-with-java-14-and-micronaut-inside-serverless-containers-on-cloud-run/</link><pubDate>Tue, 24 Mar 2020 15:59:29 +0100</pubDate><guid>https://glaforge.dev/posts/2020/03/24/start-the-fun-with-java-14-and-micronaut-inside-serverless-containers-on-cloud-run/</guid><description>&lt;p>Hot on the heels of the &lt;a href="https://mail.openjdk.java.net/pipermail/jdk-dev/2020-March/004089.html">announcement&lt;/a> of the general availability of JDK 14, I couldn&amp;rsquo;t resist taking it for a spin. Without messing up my environment &amp;mdash; I&amp;rsquo;ll confess I&amp;rsquo;m running 11 on my machine, but I&amp;rsquo;m still not even using everything that came past Java 8! &amp;mdash; I decided to test this new edition within the comfy setting of a Docker container.&lt;/p>
&lt;h2 id="minimal-openjdk-14-image-running-jshell">Minimal OpenJDK 14 image running JShell&lt;/h2>
&lt;p>Super easy to get started (assuming you have Docker installed on your machine), create a Dockerfile with the following content:&lt;/p></description><content:encoded>
<![CDATA[<p>Hot on the heels of the <a href="https://mail.openjdk.java.net/pipermail/jdk-dev/2020-March/004089.html">announcement</a> of the general availability of JDK 14, I couldn&rsquo;t resist taking it for a spin. Without messing up my environment &mdash; I&rsquo;ll confess I&rsquo;m running 11 on my machine, but I&rsquo;m still not even using everything that came past Java 8! &mdash; I decided to test this new edition within the comfy setting of a Docker container.</p>
<h2 id="minimal-openjdk-14-image-running-jshell">Minimal OpenJDK 14 image running JShell</h2>
<p>Super easy to get started (assuming you have Docker installed on your machine), create a Dockerfile with the following content:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dockerfile" data-lang="dockerfile"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#4070a0"> openjdk:14</span><span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">CMD</span> [<span style="color:#4070a0">&#34;jshell&#34;</span>]<span style="">
</span></span></span></code></pre></div><p>Only two lines: one to declare an <a href="https://hub.docker.com/_/openjdk">OpenJDK base image</a> with the 14 tag, and one to launch the <a href="https://docs.oracle.com/en/java/javase/14/jshell/introduction-jshell.html">JShell</a> REPL (introduced in Java 9).</p>
<p>Build and tag the image with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ docker build -t 14fun .
</span></span></code></pre></div><p>I called it 14fun, because you could almost pronounce it &ldquo;one for the fun&rdquo;! And then you can run this container image interactively with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ docker run -it 14fun
</span></span></code></pre></div><p>Then you will land directly in JShell, and can try a hello world of sorts:</p>
<pre tabindex="0"><code>Mar 20, 2020 9:17:28 AM java.util.prefs.FileSystemPreferences$1 run
INFO: Created user preferences directory.
| Welcome to JShell -- Version 14
| For an introduction type: /help intro
jshell&gt; System.out.println(&#34;Stay at home!&#34;)
Stay at home!
</code></pre><p>You can enter /exit to quit the REPL.</p>
<p>And you certainly noticed that we&rsquo;re indeed on version 14, congrats!</p>
<h2 id="new-and-preview-features">New and preview features</h2>
<p>If you read the <a href="https://mail.openjdk.java.net/pipermail/jdk-dev/2020-March/004089.html">announcement</a>, you will have remarked that some of the new features are not necessarily generally available, but are still only in preview mode. </p>
<p>Here&rsquo;s what&rsquo;s new:</p>
<ul>
<li><a href="https://openjdk.java.net/jeps/361">improved switch expressions</a> (standard),</li>
<li><a href="https://openjdk.java.net/jeps/305">pattern matching on instanceof</a> (preview), </li>
<li><a href="https://openjdk.java.net/jeps/359">records</a> (preview), and </li>
<li><a href="https://openjdk.java.net/jeps/368">text blocks</a> (second preview).</li>
</ul>
<p>If you want to play with those upcoming features, you have to let the Java tooling know that you want to enable them. You can do that with the &ndash;enable-preview flag. So let&rsquo;s update our Dockerfile accordingly:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dockerfile" data-lang="dockerfile"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#4070a0"> openjdk:14</span><span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">CMD</span> [<span style="color:#4070a0">&#34;jshell&#34;</span>, <span style="color:#4070a0">&#34;--enable-preview&#34;</span>]<span style="">
</span></span></span></code></pre></div><p>Rebuild and rerun the docker commands.</p>
<h3 id="text-blocks">Text blocks</h3>
<p>What about trying the <a href="https://openjdk.java.net/jeps/368">text blocks</a>? With text blocks, don&rsquo;t bother with appending strings with + operations, not forgetting the \n at the end of each line. It&rsquo;s now sweeter to write long strings spanning several lines, for example:</p>
<pre tabindex="0"><code>$ docker run -it 14fun 
Mar 20, 2020 1:12:28 PM java.util.prefs.FileSystemPreferences$1 run
INFO: Created user preferences directory.
| Welcome to JShell -- Version 14
| For an introduction type: /help intro
jshell&gt; var s = &#34;&#34;&#34;
   ...&gt; Hello everyone!
   ...&gt; How are you doing?
   ...&gt; &#34;&#34;&#34;
s ==&gt; &#34;Hello everyone!\nHow are you doing?\n&#34;
</code></pre><h3 id="records">Records</h3>
<p>Java is often criticized for its verbosity &mdash; think for instance how writing good POJOs can be tiresome, with proper equals() / hashCode() / toString() methods, constructors, getters and setters. Fortunately, IDEs help a lot here, but sometimes you really want some simple data holder classes without all the boilerplate. That&rsquo;s where <a href="https://openjdk.java.net/jeps/359">records</a> come into the picture. </p>
<p>Note however that these are not immutable (for example, like in Apache Groovy with its <a href="http://docs.groovy-lang.org/latest/html/gapi/groovy/transform/Immutable.html">@Immutable</a> transformation), if the fields in the record are mutable objects.</p>
<p>Let&rsquo;s imagine a 3D point Record, what would it look like?</p>
<pre tabindex="0"><code>jshell&gt; record Point3D(double x, double y, double z) { }
| created record Point3D

jshell&gt; var p1 = new Point3D(0, 1, 2)
p1 ==&gt; Point3D[x=0.0, y=1.0, z=2.0]

jshell&gt; var p2 = new Point3D(0, 1, 2)
p2 ==&gt; Point3D[x=0.0, y=1.0, z=2.0]

jshell&gt; p1.equals(p2)
$5 ==&gt; true
</code></pre><p>Notice the toString() representation, and that equals() is implemented comparing the values of each field.</p>
<h3 id="improved-switch-expressions">Improved switch expressions</h3>
<p>Switch statements are&hellip; indeed statements, not expressions. It means they didn&rsquo;t so far return any value, or couldn&rsquo;t be passed as parameter values to methods. </p>
<p>Times they are <a href="https://en.wikipedia.org/wiki/The_Times_They_Are_a-Changin%27_(Bob_Dylan_album)">a-changin</a>! Switch borrows the arrow syntax from lambdas to get a&hellip; break from break! And they can be used as values too!</p>
<pre tabindex="0"><code>jshell&gt; var day = &#34;Saturday&#34;
day ==&gt; &#34;Saturday&#34;

jshell&gt; var isWeekend = switch (day) {
   ...&gt; case &#34;Monday&#34;, &#34;Tuesday&#34;, &#34;Wednesday&#34;, 
   ...&gt; &#34;Thursday&#34;, &#34;Friday&#34; -&gt; false;
   ...&gt; case &#34;Saturday&#34;, &#34;Sunday&#34; -&gt; true;
   ...&gt; default -&gt; false;
   ...&gt; }
isWeekend ==&gt; true
</code></pre><h3 id="pattern-matching-on-instanceof">Pattern matching on instanceof</h3>
<p>Working with Apache Groovy for years, whether with its static type checking or dynamic nature, I&rsquo;m quite used to skipping the redundant cast inside if (someObj instanceof SomeObject) {} blocks, thanks to <a href="http://docs.groovy-lang.org/latest/html/documentation/core-semantics.html#_instanceof_inference">smart type inference</a> and flow typing. Java 14 takes a slightly different approach to this problem by with its <a href="https://openjdk.java.net/jeps/305">pattern matching on instanceof</a>, introducing a new local variable of the right type, rather than assuming the variable itself is of the right type. Well, it&rsquo;s better explained with an example:</p>
<pre tabindex="0"><code>jshell&gt; String name = &#34; Guillaume &#34;
name ==&gt; &#34; Guillaume &#34;

jshell&gt; if (name instanceof String nameString) {
   ...&gt; System.out.println(nameString.trim());
   ...&gt; } else {
   ...&gt; System.out.println(&#34;Not a string!&#34;);
   ...&gt; }
Guillaume
</code></pre><h2 id="jdk-14-in-a-serverless-container-with-cloud-run">JDK 14 in a serverless container, with Cloud Run</h2>
<p>Together we discovered the cool new syntax enhancements and constructs, and how we can play with them inside a Docker container image. But what about deploying some Java 14 powered containerized app in the cloud, in a serverless fashion? (ie. transparent scaling from 0 to 1, 1 to n, and back, paying only for what you use). For that purpose, you can easily deploy and scale containers in the cloud thanks to <a href="https://cloud.run/">Cloud Run</a>.</p>
<p>With the launch of Java / JDK 14, also came the first <a href="https://objectcomputing.com/news/2020/03/20/micronaut-20-milestone-1-released">2.0 milestone of the lovely and powerful Micronaut framework</a>! Micronaut is probably the best framework for serverless microservices on the market, thanks to its awesome performance, lightness, in particular regarding super fast startup times. So it&rsquo;s the right occasion to have fun with <a href="https://micronaut.io/">Micronaut</a> again. So let&rsquo;s build a Java 14 application, with Micronaut, running on Cloud Run.</p>
<h3 id="create-a-micronaut-app">Create a Micronaut app</h3>
<p>To get started, have a look at the <a href="https://docs.micronaut.io/latest/guide/index.html#buildCLI">installation guide</a> for Micronaut. In a nutshell, it&rsquo;s using the <a href="https://sdkman.io/">Sdkman</a> tool to manage versions of various SDKs. You can install Sdkman easily:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ curl -s <span style="color:#4070a0">&#34;https://get.sdkman.io&#34;</span> | bash
</span></span></code></pre></div><p>Once installed, you can also install the Micronaut command-line:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ sdk install micronaut 2.0.0.M1
</span></span></code></pre></div><p>Next, we&rsquo;ll create an empty app named &ldquo;app&rdquo; with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ mn create-app app
</span></span></code></pre></div><p>The project will be created in the app/ subdirectory, cd into it, to also create a controller, and call it hello:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ mn create-controller hello
</span></span></code></pre></div><p>You&rsquo;ll need to implement the controller, and tweak the app/build.gradle file to enable Java 14&rsquo;s preview features.</p>
<p>Update this section at the bottom of the build file to add the &ndash;enable-preview flag:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>tasks<span style="color:#666">.</span><span style="color:#4070a0">withType</span><span style="color:#666">(</span>JavaCompile<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    options<span style="color:#666">.</span><span style="color:#4070a0">encoding</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#34;UTF-8&#34;</span>
</span></span><span style="display:flex;"><span>    options<span style="color:#666">.</span><span style="color:#4070a0">compilerArgs</span><span style="color:#666">.</span><span style="color:#4070a0">add</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;-parameters&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    options<span style="color:#666">.</span><span style="color:#4070a0">compilerArgs</span><span style="color:#666">.</span><span style="color:#4070a0">add</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;--enable-preview&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Now, open the src/main/java/app/HelloController.java class:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">package</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">app</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.micronaut.http.MediaType</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.micronaut.http.annotation.Controller</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.micronaut.http.annotation.Get</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">io.micronaut.http.annotation.Produces</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#555;font-weight:bold">@Controller</span>(<span style="color:#4070a0">&#34;/&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">HelloController</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Produces</span>(MediaType.<span style="color:#4070a0">TEXT_HTML</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#555;font-weight:bold">@Get</span>(<span style="color:#4070a0">&#34;/{day}&#34;</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">index</span>(String<span style="color:#bbb"> </span>day)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>isWeekend<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">switch</span>(day.<span style="color:#4070a0">toLowerCase</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;saturday&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;sunday&#34;</span><span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">true</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">case</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;monday&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;tuesday&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;wednesday&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                 </span><span style="color:#4070a0">&#34;thursday&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;friday&#34;</span><span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">false</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">default</span><span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">false</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>};<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>String.<span style="color:#4070a0">format</span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">                        It&#39;s %s, it is %s the weekend!
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">           &#34;&#34;&#34;</span>,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">           </span>day,<span style="color:#bbb"> </span>(isWeekend<span style="color:#bbb"> </span><span style="color:#666">?</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#34;</span><span style="color:#bbb"> </span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;not&#34;</span>));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Notice how you take advantage of the improved switch expression and the text block!</p>
<h3 id="create-a-docker-image">Create a Docker image</h3>
<p>Micronaut&rsquo;s project template comes with a default Dockerfile, but update it to look like this:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dockerfile" data-lang="dockerfile"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#4070a0"> openjdk:14</span><span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">WORKDIR</span><span style="color:#4070a0"> /app</span><span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">COPY</span> ./ ./<span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">RUN</span> ./gradlew shadowJar<span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">EXPOSE</span><span style="color:#4070a0"> 8080</span><span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">CMD</span> [<span style="color:#4070a0">&#34;java&#34;</span>, <span style="color:#4070a0">&#34;--enable-preview&#34;</span>, <span style="color:#4070a0">&#34;-jar&#34;</span>, <span style="color:#4070a0">&#34;build/libs/app-0.1-all.jar&#34;</span>]<span style="">
</span></span></span></code></pre></div><p>Then build this container image (with your name of choice) with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker build -t IMAGE_NAME .
</span></span></code></pre></div><p>And check that it runs fine with this docker run command:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker run -p 8080:8080 -it IMAGE_NAME
</span></span></code></pre></div><p>Then head over to <a href="http://127.0.0.1/Monday">http://127.0.0.1/Monday</a> or <a href="http://127.0.0.1/Sunday">http://127.0.0.1/Sunday</a> to see if it works fine.</p>
<p>So you now have a working Micronaut 2.0 application, running on JDK 14, using some of the new and preview features of Java 14! Congrats!</p>
<h3 id="scaling-your-container-image-in-the-cloud">Scaling your container image in the cloud</h3>
<p>Time to deploy your Java 14-powered Micronaut web application into the cloud, on Cloud Run. Why <a href="https://cloud.google.com/run/">Cloud Run</a>? Because with Cloud Run, you can easily push a container in production in matters of seconds. It abstracts away all the infrastructure, so you don&rsquo;t have to worry about it. Google Cloud Platform handles it for you, so you can focus on your code instead. You pay proportionally to your usage: it&rsquo;s serveless, so if nobody pings your app, you won&rsquo;t pay anything as no container will be running. But as traffic ramps up, one or more containers will be lined up to serve your requests.</p>
<p>If you haven&rsquo;t already, you can get started on Google Cloud Platform with its <a href="https://cloud.google.com/free">free trial</a> (and free quota). For this tutorial however, you need to create a billing account. Once you have an account ready, create a new GCP project in the Google Cloud <a href="https://console.cloud.google.com/">console</a>. Head over to the Cloud Run section, from the hamburger menu, and click on the &ldquo;Enable API&rdquo; button.</p>
<p>Last thing before heading to the command-line, install the <a href="https://cloud.google.com/sdk/docs/quickstarts">gcloud SDK</a> command-line to work from your terminal. Once gcloud is installed, you can login with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud auth login
</span></span></code></pre></div><p>Set the project name to the one you created in the console:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud config <span style="color:#007020">set</span> project YOUR_PROJECT_ID
</span></span></code></pre></div><p>You&rsquo;ll be using the fully-managed version of Cloud Run:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud config <span style="color:#007020">set</span> run/platform managed
</span></span></code></pre></div><p>Define a default region, for me, that&rsquo;s gonna be europe-west1</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud config <span style="color:#007020">set</span> run/region REGION
</span></span></code></pre></div><p>It&rsquo;s possible to also build container images in <a href="https://cloud.google.com/cloud-build">Cloud Build</a> (see some <a href="https://cloud.google.com/run/docs/quickstarts/build-and-deploy">instructions</a> that show this), but here you are using Docker locally to build your images. So let&rsquo;s configure the Docker integration and Container Registry access with the following commands:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud auth configure-docker
</span></span><span style="display:flex;"><span>gcloud components install docker-credential-gcr
</span></span></code></pre></div><p>Tag your image with the following naming convention:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker build . --tag gcr.io/YOUR_PROJECT_ID/IMAGE_NAME
</span></span></code></pre></div><p>Let&rsquo;s push our image to Container Registry (and change the image and project names accordingly):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker push gcr.io/YOUR_PROJECT_ID/IMAGE_NAME
</span></span><span style="display:flex;"><span>gcloud run deploy weekend-service<span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span> --image gcr.io/YOUR_PROJECT_ID/IMAGE_NAME
</span></span><span style="display:flex;"><span> --allow-unauthenticated
</span></span></code></pre></div><p>You should see output similar to this, showing the URL where you can access your app:</p>
<pre tabindex="0"><code>Deploying container to Cloud Run service [weekend-service] in project [YOUR_PROJECT_ID] region [europe-west1]
✓ Deploying new service... Done.
 ✓ Creating Revision...
 ✓ Routing traffic...
 ✓ Setting IAM Policy...
Done.
Service [weekend-service] revision [weekend-service-00001-xig] has been deployed and is serving 100 percent of traffic at https://weekend-service-brxby8yoda-ew.a.run.app
</code></pre><p>Navigate to that URL, append the name of the day, and check whether it&rsquo;s weekend time!</p>
<h2 id="and-voilà">And voilà! </h2>
<p>Less than a minute later, your Java 14 + Micronaut container app has been deployed to Cloud Run. Automatically, you got a secured HTTPS endpoint for your app (you can also provide your own domain name), without bothering with the infrastructure and scaling aspects.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Serverless tip #7 — Create mini APIs with Cloud Functions and Express routing</title><link>https://glaforge.dev/posts/2019/12/17/serverless-tip-7-create-mini-apis-with-cloud-functions-and-express-routing/</link><pubDate>Tue, 17 Dec 2019 16:04:34 +0100</pubDate><guid>https://glaforge.dev/posts/2019/12/17/serverless-tip-7-create-mini-apis-with-cloud-functions-and-express-routing/</guid><description>&lt;p>Requirements:&lt;/p>
&lt;ul>
&lt;li>an existing Google Cloud Platform account and project&lt;/li>
&lt;li>Cloud Functions should be enabled for that project&lt;/li>
&lt;/ul>
&lt;p>Compared to the previous tip when using Exress&amp;rsquo; request path attribute, we can take advantage of Express routing.&lt;/p>
&lt;p>So to support the following paths:&lt;/p>
&lt;pre tabindex="0">&lt;code>https://us-central1-myproject.cloudfunctions.net/api/customers
https://us-central1-myproject.cloudfunctions.net/api/customers/32
https://us-central1-myproject.cloudfunctions.net/api/customers/32/address
&lt;/code>&lt;/pre>&lt;p>We can have our functions require Express by adding Express in &lt;code>package.json&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>  &lt;span style="color:#062873;font-weight:bold">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;mini-api-router&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>  &lt;span style="color:#062873;font-weight:bold">&amp;#34;version&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;0.0.1&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>  &lt;span style="color:#062873;font-weight:bold">&amp;#34;dependencies&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>    &lt;span style="color:#062873;font-weight:bold">&amp;#34;express&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;^4.17.1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>  }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then we can require that dependency in our new functions script:&lt;/p></description><content:encoded>
<![CDATA[<p>Requirements:</p>
<ul>
<li>an existing Google Cloud Platform account and project</li>
<li>Cloud Functions should be enabled for that project</li>
</ul>
<p>Compared to the previous tip when using Exress&rsquo; request path attribute, we can take advantage of Express routing.</p>
<p>So to support the following paths:</p>
<pre tabindex="0"><code>https://us-central1-myproject.cloudfunctions.net/api/customers
https://us-central1-myproject.cloudfunctions.net/api/customers/32
https://us-central1-myproject.cloudfunctions.net/api/customers/32/address
</code></pre><p>We can have our functions require Express by adding Express in <code>package.json</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;name&#34;</span>: <span style="color:#4070a0">&#34;mini-api-router&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;version&#34;</span>: <span style="color:#4070a0">&#34;0.0.1&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;dependencies&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;express&#34;</span>: <span style="color:#4070a0">&#34;^4.17.1&#34;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Then we can require that dependency in our new functions script:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// we use express explicitly
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">const</span> express <span style="color:#666">=</span> require(<span style="color:#4070a0">&#39;express&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> app <span style="color:#666">=</span> express();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// some customer data retrieved from Firestore or elsewhere
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">const</span> customers <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;32&#34;</span><span style="color:#666">:</span> { name<span style="color:#666">:</span> <span style="color:#4070a0">&#39;Alice&#39;</span>, address<span style="color:#666">:</span> <span style="color:#4070a0">&#39;21 Jump Street&#39;</span> },
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;33&#34;</span><span style="color:#666">:</span> { name<span style="color:#666">:</span> <span style="color:#4070a0">&#39;Bob&#39;</span>, address<span style="color:#666">:</span> <span style="color:#4070a0">&#39;1 Main Street&#39;</span> }
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// this time we can define the path easily
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>app.get(<span style="color:#4070a0">&#39;/&#39;</span>, (req, res) =&gt; 
</span></span><span style="display:flex;"><span>        res.send(<span style="color:#4070a0">&#39;Hello World!&#39;</span>));
</span></span><span style="display:flex;"><span>app.get(<span style="color:#4070a0">&#39;/customers&#39;</span>, (req, res) =&gt; 
</span></span><span style="display:flex;"><span>        res.status(<span style="color:#40a070">200</span>).json(customers).end());
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// we can also specify path variables like :id 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// that we can retrieve via the request params object
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>app.get(<span style="color:#4070a0">&#39;/customers/:id&#39;</span>, (req, res) =&gt; 
</span></span><span style="display:flex;"><span>        res.status(<span style="color:#40a070">200</span>).json(customers[req.params.id]).end());
</span></span><span style="display:flex;"><span>app.get(<span style="color:#4070a0">&#39;/customers/:id/address&#39;</span>, (req, res) =&gt; 
</span></span><span style="display:flex;"><span>        res.status(<span style="color:#40a070">200</span>).json({address<span style="color:#666">:</span> customers[req.params.id].address}).end());
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// we need to export the app object for Cloud Functions to expose it
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>exports.api <span style="color:#666">=</span> app;
</span></span></code></pre></div><p>More information:</p>
<ul>
<li><a href="https://expressjs.com/">Express framework</a></li>
<li>Express <a href="http://expressjs.com/en/guide/routing.html#routing">routing</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Serverless tip #6 — Create a mini web API with Cloud Functions</title><link>https://glaforge.dev/posts/2019/12/17/serverless-tip-6-create-a-mini-web-api-with-cloud-functions/</link><pubDate>Tue, 17 Dec 2019 16:01:33 +0100</pubDate><guid>https://glaforge.dev/posts/2019/12/17/serverless-tip-6-create-a-mini-web-api-with-cloud-functions/</guid><description>&lt;p>Requirements:&lt;/p>
&lt;ul>
&lt;li>an existing Google Cloud Platform account and project&lt;/li>
&lt;li>Cloud Functions should be enabled for that project&lt;/li>
&lt;/ul>
&lt;p>We often use individual HTTP &lt;a href="https://cloud.google.com/functions/">Cloud Functions&lt;/a> as a single endpoint, and we pass data to the functions with either query parameters, or via a POST body payload. Although it&amp;rsquo;s a good practice to keep the scope of a function small, however, you can easily write mini Web APIs for a given function, with different paths for different needs, like with usual Web frameworks.&lt;/p></description><content:encoded>
<![CDATA[<p>Requirements:</p>
<ul>
<li>an existing Google Cloud Platform account and project</li>
<li>Cloud Functions should be enabled for that project</li>
</ul>
<p>We often use individual HTTP <a href="https://cloud.google.com/functions/">Cloud Functions</a> as a single endpoint, and we pass data to the functions with either query parameters, or via a POST body payload. Although it&rsquo;s a good practice to keep the scope of a function small, however, you can easily write mini Web APIs for a given function, with different paths for different needs, like with usual Web frameworks.</p>
<p>So instead of having just a single endpoint with:</p>
<pre tabindex="0"><code>https://us-central1-myproject.cloudfunctions.net/myfunction
</code></pre><p>You can have sub-paths below the name of your function:</p>
<pre tabindex="0"><code>https://us-central1-myproject.cloudfunctions.net/myapi/customers
https://us-central1-myproject.cloudfunctions.net/myapi/customers/32
https://us-central1-myproject.cloudfunctions.net/myapi/customers/32/address
</code></pre><p>Let&rsquo;s have a look at the Node functions runtime, and how you can implement this approach. The key trick here is to use the request path: req.path, which will give you the <code>/customers/32</code> part of the fully qualified URL.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// some customer data retrieved from Firestore or elsewhere
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">const</span> customers <span style="color:#666">=</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;32&#34;</span><span style="color:#666">:</span> { name<span style="color:#666">:</span> <span style="color:#4070a0">&#39;Alice&#39;</span>, address<span style="color:#666">:</span> <span style="color:#4070a0">&#39;21 Jump Street&#39;</span> },
</span></span><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;33&#34;</span><span style="color:#666">:</span> { name<span style="color:#666">:</span> <span style="color:#4070a0">&#39;Bob&#39;</span>, address<span style="color:#666">:</span> <span style="color:#4070a0">&#39;1 Main Street&#39;</span> }
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>exports.myapi <span style="color:#666">=</span> (req, res) =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">if</span> (req.path.startsWith(<span style="color:#4070a0">&#39;/customers&#39;</span>)) {
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">const</span> pathElements <span style="color:#666">=</span> req.path.split(<span style="color:#4070a0">&#39;/&#39;</span>) <span style="color:#60a0b0;font-style:italic">// split along the slashes
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>      .filter(e =&gt; e)                        <span style="color:#60a0b0;font-style:italic">// remove the empty strings in the array
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>      .splice(<span style="color:#40a070">1</span>);                            <span style="color:#60a0b0;font-style:italic">// remove the first &#34;customers&#34; element
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#60a0b0;font-style:italic">// path: /customers
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#007020;font-weight:bold">if</span> (pathElements.length <span style="color:#666">==</span> <span style="color:#40a070">0</span>) { 
</span></span><span style="display:flex;"><span>      <span style="color:#60a0b0;font-style:italic">// return all customers
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>      res.status(<span style="color:#40a070">200</span>).json(customers).end();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// path: /customers/32
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#007020;font-weight:bold">else</span> <span style="color:#007020;font-weight:bold">if</span> (pathElements.length <span style="color:#666">==</span> <span style="color:#40a070">1</span>) {
</span></span><span style="display:flex;"><span>      res.status(<span style="color:#40a070">200</span>).json(customers[pathElements[<span style="color:#40a070">0</span>]]).end();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// path: /customers/33/address
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#007020;font-weight:bold">else</span> <span style="color:#007020;font-weight:bold">if</span> (pathElements.length <span style="color:#666">==</span> <span style="color:#40a070">2</span> <span style="color:#666">&amp;&amp;</span> pathElements[<span style="color:#40a070">1</span>] <span style="color:#666">==</span> <span style="color:#4070a0">&#34;address&#34;</span>) {
</span></span><span style="display:flex;"><span>      res.status(<span style="color:#40a070">200</span>).json({address<span style="color:#666">:</span> customers[pathElements[<span style="color:#40a070">0</span>]].address}).end();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  res.status(<span style="color:#40a070">404</span>).send(<span style="color:#4070a0">&#39;Unknown path&#39;</span>).end();
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>In the Node.JS runtime, Cloud Functions uses the Express framework under the hood. We have access to the request object, which has lots of useful attributes, including the path.</p>
<p>In this simplistic example, we are using this path attribute directly, but it&rsquo;s also possible to use more advanced routing capabilities, as we shall see in a forthcoming tip.</p>
<p>More information</p>
<ul>
<li><a href="https://expressjs.com/">Express framework</a></li>
<li>Node&rsquo;s request <a href="https://expressjs.com/en/api.html#req.path">path</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Serverless tip #5 — How to invoke a secured Cloud Run service locally</title><link>https://glaforge.dev/posts/2019/12/11/serverless-tip-5-how-to-invoke-a-secured-cloud-run-service-locally/</link><pubDate>Wed, 11 Dec 2019 16:36:39 +0100</pubDate><guid>https://glaforge.dev/posts/2019/12/11/serverless-tip-5-how-to-invoke-a-secured-cloud-run-service-locally/</guid><description>&lt;p>Requirements:&lt;/p>
&lt;ul>
&lt;li>an existing Google Cloud Platform account with a project&lt;/li>
&lt;li>you have enabled the Cloud Run service and already deployed a container image&lt;/li>
&lt;li>your local environment&amp;rsquo;s gcloud is already configured to point at your GCP project&lt;/li>
&lt;/ul>
&lt;p>By default, when you deploy a &lt;a href="https://cloud.run/">Cloud Run&lt;/a> service, it is secured by default, unless you use the &amp;ndash;allow-unauthenticated flag when using the gcloud command-line (or the appropriate checkbox on the Google Cloud Console).&lt;/p>
&lt;p>But once deployed, if you want to call it locally from your development machine, for testing purpose, you&amp;rsquo;ll have to be authenticated.&lt;/p></description><content:encoded>
<![CDATA[<p>Requirements:</p>
<ul>
<li>an existing Google Cloud Platform account with a project</li>
<li>you have enabled the Cloud Run service and already deployed a container image</li>
<li>your local environment&rsquo;s gcloud is already configured to point at your GCP project</li>
</ul>
<p>By default, when you deploy a <a href="https://cloud.run/">Cloud Run</a> service, it is secured by default, unless you use the &ndash;allow-unauthenticated flag when using the gcloud command-line (or the appropriate checkbox on the Google Cloud Console).</p>
<p>But once deployed, if you want to call it locally from your development machine, for testing purpose, you&rsquo;ll have to be authenticated.</p>
<p>If you look at the Cloud Console, alongside the URL of the service, you can hover the little icon next to the URL, and you&rsquo;ll see the a pop-up showing how you can invoke that service with a curl command:</p>
<p><figure>
  <a href="#img-fa2e1e8051d795b94a85ba4597cf469d">
    <img src="/img/serverless-tips/st5-cr-call-authenticated-service.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-fa2e1e8051d795b94a85ba4597cf469d">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/serverless-tips/st5-cr-call-authenticated-service.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -H <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    <span style="color:#4070a0">&#34;Authorization: Bearer </span><span style="color:#007020;font-weight:bold">$(</span>gcloud auth print-identity-token<span style="color:#007020;font-weight:bold">)</span><span style="color:#4070a0">&#34;</span> <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    https://authenticated-x2rq3lgmra-uc.a.run.app
</span></span></code></pre></div><p>Note how a bearer token generated by the gcloud command is passed as header to the curl request.</p>
<p>More information</p>
<ul>
<li><a href="https://cloud.run/">https://cloud.run</a>, the serverless container platform</li>
<li><a href="https://cloud.google.com/run/docs/authenticating/overview">Authentication overview documentation</a></li>
<li><a href="https://cloud.google.com/run/docs/authenticating/public">Unauthenticated invocations</a> of your services</li>
<li><a href="https://cloud.google.com/sdk/gcloud/reference/auth/print-identity-token">gcloud auth print-identity-token</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>8 production-ready features you'll find in Cloud Run fully managed</title><link>https://glaforge.dev/posts/2019/12/11/8-production-ready-features-you-ll-find-in-cloud-run-fully-managed/</link><pubDate>Wed, 11 Dec 2019 13:42:59 +0100</pubDate><guid>https://glaforge.dev/posts/2019/12/11/8-production-ready-features-you-ll-find-in-cloud-run-fully-managed/</guid><description>&lt;p>Since we &lt;a href="https://cloud.google.com/blog/products/serverless/announcing-cloud-run-the-newest-member-of-our-serverless-compute-stack">launched Cloud Run&lt;/a> at Google Cloud Next in April, developers have discovered that &amp;ldquo;serverless&amp;rdquo; and &amp;ldquo;containers&amp;rdquo; run well together. With &lt;a href="https://cloud.google.com/run/">Cloud Run&lt;/a>, not only do you benefit from fully managed infrastructure, up and down auto-scaling, and pay-as-you-go pricing, but you&amp;rsquo;re also able to package your workload however you like, inside a stateless container listening for incoming requests, with any language, runtime, or library of your choice. And you get all this without compromising portability, thanks to its &lt;a href="https://knative.dev/">Knative&lt;/a> open-source underpinnings. &lt;/p></description><content:encoded>
<![CDATA[<p>Since we <a href="https://cloud.google.com/blog/products/serverless/announcing-cloud-run-the-newest-member-of-our-serverless-compute-stack">launched Cloud Run</a> at Google Cloud Next in April, developers have discovered that &ldquo;serverless&rdquo; and &ldquo;containers&rdquo; run well together. With <a href="https://cloud.google.com/run/">Cloud Run</a>, not only do you benefit from fully managed infrastructure, up and down auto-scaling, and pay-as-you-go pricing, but you&rsquo;re also able to package your workload however you like, inside a stateless container listening for incoming requests, with any language, runtime, or library of your choice. And you get all this without compromising portability, thanks to its <a href="https://knative.dev/">Knative</a> open-source underpinnings. </p>
<p>Many Google Cloud customers already use Cloud Run in production, for example, to deploy public websites or APIs, or as a way to perform fast and lightweight data transformations or background operations. </p>
<p><em>&ldquo;Cloud Run promises to dramatically reduce the operational complexity of deploying containerized software. The ability to put an automatically scaling service in production with one command is very attractive.&quot;</em> - Jamie Talbot, Principal Engineer at Mailchimp.</p>
<p><a href="https://cloud.google.com/blog/products/serverless/knative-based-cloud-run-services-are-ga">Cloud Run recently became generally available</a>, as both a fully managed platform or on <a href="https://cloud.google.com/anthos/">Anthos</a>, and offers a bunch of new features. What are those new capabilities? Today, let&rsquo;s take a look at what&rsquo;s new in the fully managed Cloud Run platform.</p>
<h2 id="1-service-level-agreement">1. Service level agreement</h2>
<p>With general availability, Cloud Run now comes with a <a href="https://cloud.google.com/run/sla">Service Level Agreement</a> (SLA). In addition, it now offers <a href="https://cloud.google.com/terms/service-terms">data location commitments</a> that allow you to store customer data in a specific region/multi-region. </p>
<h2 id="2-available-in-9-gcp-regions">2. Available in 9 GCP regions</h2>
<p>In addition to South Carolina, Iowa, Tokyo, and Belgium, in the coming weeks, you&rsquo;ll also be able to deploy containers to Cloud Run in North Virginia, Oregon, Netherlands, Finland, and Taiwan, for a total of nine <a href="https://cloud.google.com/about/locations">cloud regions</a>.</p>
<p><figure>
  <a href="#img-f7b1614529316f586eac6a9a3e1c4325">
    <img src="/img/8cr/Cloud_run_regions.max-2000x2000.png"
      alt="/img/8cr/Cloud_run_regions.max-2000x2000.png"
       />
  </a>
  <figcaption>/img/8cr/Cloud_run_regions.max-2000x2000.png</figcaption>
</figure>
<div class="lightbox" id="img-f7b1614529316f586eac6a9a3e1c4325">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/8cr/Cloud_run_regions.max-2000x2000.png"
    alt="/img/8cr/Cloud_run_regions.max-2000x2000.png"
     />
  <div class="lightbox-caption">/img/8cr/Cloud_run_regions.max-2000x2000.png</div>
</div>
</p>
<h2 id="3-max-instances">3. Max instances</h2>
<p>Auto-scaling can be magic, but there are times when you want to limit the maximum number of instances of your Cloud Run services, for example, to limit costs. Or imagine a backend service like a database is limited to a certain number of connections&mdash;you might want to limit the number of instances that can connect to that service. With the <a href="https://cloud.google.com/run/docs/configuring/max-instances">max instance</a> feature, you can now set such a limit.</p>
<p>Use the Cloud Console or Cloud SDK to set this limit:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud run services update SERVICE-NAME --max-instances <span style="color:#40a070">42</span>
</span></span></code></pre></div><h2 id="4-more-secure-https-only">4. More secure: HTTPS only</h2>
<p>All fully managed Cloud Run services receive a stable and secure URL. Cloud Run now only accepts secure HTTPS connection and redirects any HTTP connection to the HTTPS endpoint. </p>
<p>But having an HTTPS endpoint does not mean that your service is publicly accessible&mdash;you are in control and can opt into <a href="https://cloud.google.com/run/docs/authenticating/public">allowing public access</a> to your service. Alternatively, you can <a href="https://cloud.google.com/run/docs/authenticating/overview">require authentication</a> by leveraging the &ldquo;Cloud Run Invoker&rdquo; IAM role.</p>
<h2 id="5-unary-grpc-protocol-support">5. Unary gRPC protocol support</h2>
<p>Cloud Run now lets you deploy and run <a href="https://grpc.io/docs/guides/concepts/">unary gRPC</a> services (i.e., non-streaming gRPC), allowing your microservices to leverage this RPC framework. </p>
<p>To learn more, read Peter Malinas&rsquo; tutorial on <a href="https://medium.com/@petomalina/%EF%B8%8Fserverless-grpc-with-cloud-run-bab3622a47da">Serverless gRPC with Cloud Run</a> using Go, as well as Ahmet Alp Balkan&rsquo;s article on <a href="https://ahmet.im/blog/grpc-auth-cloud-run/">gRPC authentication on Cloud Run</a>.</p>
<h2 id="6-new-metrics-to-track-your-instances">6. New metrics to track your instances</h2>
<p>Out of the box, Cloud Run integrates with <a href="https://cloud.google.com/monitoring/">Stackdriver Monitoring</a>. From within the Google Cloud Console, the Cloud Run page now includes a new &ldquo;Metrics&rdquo; tab that shows charts of key performance indicators for your Cloud Run service: requests per second, request latency, used instance time, CPU and memory.</p>
<p>A new built-in Stackdriver metric called <a href="https://cloud.google.com/monitoring/api/metrics_gcp#gcp-run"><code>container/billable_instance_time</code></a> gives you insights into the number of container instances for a service, with the billable time aggregated from all container instances.</p>
<p><figure>
  <a href="#img-5edda3b8f7af1f055bc291c38b19ce70">
    <img src="/img/8cr/billable_container_instance_time.max-1200x1200.jpg"
      alt="/img/8cr/billable_container_instance_time.max-1200x1200.jpg"
       />
  </a>
  <figcaption>/img/8cr/billable_container_instance_time.max-1200x1200.jpg</figcaption>
</figure>
<div class="lightbox" id="img-5edda3b8f7af1f055bc291c38b19ce70">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/8cr/billable_container_instance_time.max-1200x1200.jpg"
    alt="/img/8cr/billable_container_instance_time.max-1200x1200.jpg"
     />
  <div class="lightbox-caption">/img/8cr/billable_container_instance_time.max-1200x1200.jpg</div>
</div>
</p>
<h2 id="7-labels">7. Labels</h2>
<p>Like the bibs that identify the runners in a race, GCP <a href="https://cloud.google.com/run/docs/configuring/labels">labels</a> can help you easily identify a set of services, break down costs, or distinguish different environments.</p>
<p>You can set labels from the Cloud Run service list page in Cloud Console, or update labels with this command and flag:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud run services update SERVICE-NAME --update-labels <span style="color:#bb60d5">KEY</span><span style="color:#666">=</span>VALUE
</span></span></code></pre></div><h2 id="8-terraform-support">8. Terraform support</h2>
<p>Finally, if you practice <a href="https://cloud.google.com/solutions/infrastructure-as-code/">Infrastructure as Code</a>, you&rsquo;ll be glad to know that <a href="https://www.terraform.io/docs/providers/google/r/cloud_run_service.html">Terraform now  supports Cloud Run</a>, allowing you to provision Cloud Run services from a Terraform configuration. </p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">resource</span> <span style="color:#4070a0">&#34;google_cloud_run_service&#34; &#34;default&#34;</span> {
</span></span><span style="display:flex;"><span>    name     <span style="color:#666">=</span> <span style="color:#4070a0">&#34;hello&#34;</span>
</span></span><span style="display:flex;"><span>    location <span style="color:#666">=</span> <span style="color:#4070a0">&#34;us-central1&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">template</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">spec</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#007020;font-weight:bold">containers</span> {
</span></span><span style="display:flex;"><span>                image <span style="color:#666">=</span> <span style="color:#4070a0">&#34;gcr.io/cloudrun/hello&#34;</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="ready-set-go">Ready, set, go!</h2>
<p>The baton is now in your hands. To start deploying your container images to Cloud Run, head over to our quickstart guides on <a href="https://cloud.google.com/run/docs/quickstarts/build-and-deploy">building and deploying your images</a>. With the <a href="https://cloud.google.com/free/">always free tier</a> and the $300 credit for new GCP accounts, you&rsquo;re ready to take Cloud Run for a spin. To learn more, there&rsquo;s the <a href="https://cloud.google.com/run/docs/">documentation</a> of course, as well as the <a href="https://github.com/GoogleCloudPlatform/cloud-run-samples">numerous samples</a> with different language runtimes (don&rsquo;t miss the &ldquo;Run on Google Cloud&rdquo; <a href="https://github.com/GoogleCloudPlatform/cloud-run-button">button</a> to automatically deploy your code). In addition, be sure to check out the community-contributed resources on the <a href="https://github.com/steren/awesome-cloudrun">Awesome Cloud Run</a> github project. We&rsquo;re looking forward to seeing what you build and deploy!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Serverless tip #4 — Discover the full URL of your deployed Cloud Run services with gcloud format flag</title><link>https://glaforge.dev/posts/2019/12/05/serverless-tip-4-discover-the-full-url-of-your-deployed-cloud-run-services-with-gcloud-format-flag/</link><pubDate>Thu, 05 Dec 2019 16:39:56 +0100</pubDate><guid>https://glaforge.dev/posts/2019/12/05/serverless-tip-4-discover-the-full-url-of-your-deployed-cloud-run-services-with-gcloud-format-flag/</guid><description>&lt;p>Requirements:&lt;/p>
&lt;ul>
&lt;li>an existing Google Cloud Platform account&lt;/li>
&lt;li>you have enabled the Cloud Run service and deployed already a container image&lt;/li>
&lt;/ul>
&lt;p>One of the nice things with Cloud Run is that when you deploy your services, you get a URL like &lt;a href="https://myservice-8oafjf26aq-ew.a.run.app/">https://myservice-8oafjf26aq-ew.a.run.app/&lt;/a>, with a certificate, on the run.app domain name, etc.&lt;/p>
&lt;p>You see the name of the service: myservice, the region shortcut where it was deployed: ew (Europe West), and then .a.run.app. However, you can&amp;rsquo;t guess ahead of time what the final URL will be, as there is a randomly generated part in the URL (here: &lt;code>8oafjf26aq&lt;/code>). Let&amp;rsquo;s see how we can discover this whole URL.&lt;/p></description><content:encoded>
<![CDATA[<p>Requirements:</p>
<ul>
<li>an existing Google Cloud Platform account</li>
<li>you have enabled the Cloud Run service and deployed already a container image</li>
</ul>
<p>One of the nice things with Cloud Run is that when you deploy your services, you get a URL like <a href="https://myservice-8oafjf26aq-ew.a.run.app/">https://myservice-8oafjf26aq-ew.a.run.app/</a>, with a certificate, on the run.app domain name, etc.</p>
<p>You see the name of the service: myservice, the region shortcut where it was deployed: ew (Europe West), and then .a.run.app. However, you can&rsquo;t guess ahead of time what the final URL will be, as there is a randomly generated part in the URL (here: <code>8oafjf26aq</code>). Let&rsquo;s see how we can discover this whole URL.</p>
<p>From my terminal, I can request the list of deployed services (here, on the fully managed Cloud Run):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud run services list <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>       --platform managed
</span></span></code></pre></div><p>It&rsquo;s going to show me something like the following output:</p>
<pre tabindex="0"><code>   SERVICE      REGION        URL                                        LAST DEPLOYED BY     LAST DEPLOYED AT
✔  myservice    europe-west1  https://myservice-8oafjf26aq-ew.a.run.app  myself@foobar.com    2019-11-20T15:26:39.442Z
</code></pre><p>When describing the specific service (I had to specify the region as well, but you can set it by default if needed to avoid repeating yourself):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud run services describe myservice <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>       --platform managed <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>       --region europe-west1
</span></span></code></pre></div><p>You&rsquo;ll see:</p>
<pre tabindex="0"><code>✔ Service hello in region europe-west1
https://myservice-8oafjf26aq-ew.a.run.app
Traffic:
  100%               LATEST (currently myservice-00002-dox)
Last updated on 2019-11-20T15:26:39.442Z by myself@foobar.com:
  Revision myservice-00002-dox
  Image:             gcr.io/my-sample-project/my-container-image:latest
</code></pre><p>So instead of parsing that ourselves somehow, there&rsquo;s a built-in way to get just the info we want, with the useful <code>--format</code> flag:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud run services describe myservice <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --format<span style="color:#666">=</span><span style="color:#4070a0">&#39;value(status.url)&#39;</span> <span style="color:#4070a0;font-weight:bold">\
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0;font-weight:bold"></span>    --platform managed --region europe-west1 
</span></span></code></pre></div><p>This time, in output, you&rsquo;ll get just the URL, which you can then export or reuse with other commands.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>https://myservice-8oafjf26aq-ew.a.run.app
</span></span></code></pre></div><p>The <code>glcoud</code> command provides three useful mechanisms to filter, format, or project the output and values returned. Here, we took advantage of format.</p>
<p>More information:</p>
<ul>
<li><a href="https://cloud.run/">https://cloud.run</a>, the serverless container platform</li>
<li><a href="https://cloud.google.com/blog/products/gcp/filtering-and-formatting-fun-with">Filtering and formatting fun with gcloud, GCP&rsquo;s command line interface</a></li>
<li><a href="https://cloud.google.com/sdk/gcloud/reference/topic/filters">gcloud filters</a></li>
<li><a href="https://cloud.google.com/sdk/gcloud/reference/topic/formats">gcloud formats</a></li>
<li><a href="https://cloud.google.com/sdk/gcloud/reference/topic/projections">gcloud projections</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Serverless tip #3 — Use the Cloud Run button on your Git repository to deploy your project in a click</title><link>https://glaforge.dev/posts/2019/12/04/serverless-tip-3-use-the-cloud-run-button-on-your-git-repository-to-deploy-your-project-in-a-click/</link><pubDate>Wed, 04 Dec 2019 16:44:23 +0100</pubDate><guid>https://glaforge.dev/posts/2019/12/04/serverless-tip-3-use-the-cloud-run-button-on-your-git-repository-to-deploy-your-project-in-a-click/</guid><description>&lt;p>Requirements:&lt;/p>
&lt;ul>
&lt;li>an existing Google Cloud Platform account&lt;/li>
&lt;li>a Git or Github repository containing your project&lt;/li>
&lt;li>your project can have a Dockerfile (but not mandatory)&lt;/li>
&lt;/ul>
&lt;p>With &lt;a href="https://cloud.run/">Cloud Run&lt;/a>, you can easily deploy a container image and let it scale up and down as needed, in a serverless fashion:&lt;/p>
&lt;ul>
&lt;li>No need to focus on infrastructure (provisioning servers, clusters, upgrading OS, etc.)&lt;/li>
&lt;li>Your application can scale transparently from 0 to 1, and from 1 to n (no need for a pager when your app is featured on Hackernews)&lt;/li>
&lt;li>You pay as you go, proportionally to the usage&lt;/li>
&lt;/ul>
&lt;p>If your project is hosted on Github, for example, how can you help users get started with your project? You usually explain all the steps needed to build a container image, or where to fetch a pre-made image from a hub, and then steps to actually deploy that image on the platform. But thanks to the Cloud Run button, you can add a button image on your README.md page for instance, and then users can click on it and get started with building and deploying to a GCP project automagically.&lt;/p></description><content:encoded>
<![CDATA[<p>Requirements:</p>
<ul>
<li>an existing Google Cloud Platform account</li>
<li>a Git or Github repository containing your project</li>
<li>your project can have a Dockerfile (but not mandatory)</li>
</ul>
<p>With <a href="https://cloud.run/">Cloud Run</a>, you can easily deploy a container image and let it scale up and down as needed, in a serverless fashion:</p>
<ul>
<li>No need to focus on infrastructure (provisioning servers, clusters, upgrading OS, etc.)</li>
<li>Your application can scale transparently from 0 to 1, and from 1 to n (no need for a pager when your app is featured on Hackernews)</li>
<li>You pay as you go, proportionally to the usage</li>
</ul>
<p>If your project is hosted on Github, for example, how can you help users get started with your project? You usually explain all the steps needed to build a container image, or where to fetch a pre-made image from a hub, and then steps to actually deploy that image on the platform. But thanks to the Cloud Run button, you can add a button image on your README.md page for instance, and then users can click on it and get started with building and deploying to a GCP project automagically.</p>
<p><figure>
  <a href="#img-00b4c3c75dbf681ef99ef84e3dafb221">
    <img src="https://deploy.cloud.run/button.svg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-00b4c3c75dbf681ef99ef84e3dafb221">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://deploy.cloud.run/button.svg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>If the Git repository contains a Dockerfile, it will be built using the docker build command. Otherwise, the CNCF Buildpacks (with the pack build command) will be used to build the repository.</p>
<p>The Cloud Run button github project gives extra information on the parameterization of the deploy URL of the button, for example if you want to specify a particular branch or directory.</p>
<p>More information</p>
<ul>
<li><a href="https://cloud.run/">https://cloud.run</a>, the serverless container platform</li>
<li>Cloud Run button <a href="https://github.com/GoogleCloudPlatform/cloud-run-button">github project</a></li>
<li>Cloud Run button <a href="https://cloud.google.com/blog/products/serverless/introducing-cloud-run-button-click-to-deploy-your-git-repos-to-google-cloud">announcement</a></li>
<li><a href="https://buildpacks.io/">CNFC Buildpacks</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Serverless tip #2 — Deploy an executable JVM application with gcloud without app.yaml or build tool plugin</title><link>https://glaforge.dev/posts/2019/12/03/serverless-tip-2-deploy-an-executable-jvm-application-with-gcloud-without-app-yaml-or-build-tool-plugin/</link><pubDate>Tue, 03 Dec 2019 16:46:44 +0100</pubDate><guid>https://glaforge.dev/posts/2019/12/03/serverless-tip-2-deploy-an-executable-jvm-application-with-gcloud-without-app-yaml-or-build-tool-plugin/</guid><description>&lt;p>Requirements:&lt;/p>
&lt;ul>
&lt;li>an existing Google Cloud Platform account and project&lt;/li>
&lt;li>a Java or alternative language web application&lt;/li>
&lt;li>a build that creates a standalone executable JAR file&lt;/li>
&lt;/ul>
&lt;p>Usually App Engine applications in Java are deployed with the gcloud command-line interface, or via a Maven or Gradle build plugin.
Either way, an &lt;code>app.yaml&lt;/code> file to describe your application is required to let the cloud SDK know that the project at hand is an App Engine project.&lt;/p></description><content:encoded>
<![CDATA[<p>Requirements:</p>
<ul>
<li>an existing Google Cloud Platform account and project</li>
<li>a Java or alternative language web application</li>
<li>a build that creates a standalone executable JAR file</li>
</ul>
<p>Usually App Engine applications in Java are deployed with the gcloud command-line interface, or via a Maven or Gradle build plugin.
Either way, an <code>app.yaml</code> file to describe your application is required to let the cloud SDK know that the project at hand is an App Engine project.</p>
<p>With the Java 11 runtime, however, it&rsquo;s possible to deploy a standalone executable JAR without <code>app.yaml</code>.
The <code>gcloud app deploy</code> command now takes also a path to a standalone JAR file:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud app deploy path/to/app.jar
</span></span></code></pre></div><p>App Engine will automatically assume that you are deploying to the Java 11 runtime, using an F1 instance (256MB of RAM and 600MHz of CPU).
So this deployment would be equivalent to having a simple app.yaml file as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">runtime</span>:<span style="color:#bbb"> </span>java11<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">instance_class</span>:<span style="color:#bbb"> </span>F1<span style="color:#bbb">
</span></span></span></code></pre></div><p>More information:</p>
<ul>
<li><a href="https://cloud.google.com/sdk/gcloud/reference/app/deploy">gcloud app deploy</a> command details</li>
<li><a href="https://cloud.google.com/appengine/docs/standard/java11/config/appref">app.yaml</a></li>
<li><a href="https://cloud.google.com/blog/products/application-development/app-engine-java-11-is-ga-deploy-a-jar-scale-it-all-fully-managed">Announcement and example with Maven and Spring</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Serverless tip #1 — Deploy a standalone JVM web app with Gradle and the App Engine plugin</title><link>https://glaforge.dev/posts/2019/11/29/serverless-tip-1-deploy-a-standalone-jvm-web-app-with-gradle-and-the-app-engine-plugin/</link><pubDate>Fri, 29 Nov 2019 16:49:31 +0100</pubDate><guid>https://glaforge.dev/posts/2019/11/29/serverless-tip-1-deploy-a-standalone-jvm-web-app-with-gradle-and-the-app-engine-plugin/</guid><description>&lt;p>Requirements:&lt;/p>
&lt;ul>
&lt;li>an existing Google Cloud Platform account and project&lt;/li>
&lt;li>a Java or alternative language web application&lt;/li>
&lt;li>a Gradle build that creates a standalone executable JAR file&lt;/li>
&lt;/ul>
&lt;p>In youd &lt;code>build.gradle&lt;/code> file, add the App Engine gradle plugin to your &lt;code>buildscript&lt;/code> dependencies:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-groovy" data-lang="groovy">&lt;span style="display:flex;">&lt;span>buildscript &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>    repositories &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>        jcenter&lt;span style="color:#666">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>        mavenCentral&lt;span style="color:#666">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>    &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>    dependencies &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>        classpath &lt;span style="color:#4070a0">&amp;#39;com.google.cloud.tools:appengine-gradle-plugin:2.+&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>    &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Apply the plugin, to make use of it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-groovy" data-lang="groovy">&lt;span style="display:flex;">&lt;span>apply &lt;span style="color:#002070;font-weight:bold">plugin:&lt;/span> &lt;span style="color:#4070a0">&amp;#34;com.google.cloud.tools.appengine-appyaml&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then you can configure the &lt;code>appengine&lt;/code> task to point at the standalone executable JAR:&lt;/p></description><content:encoded>
<![CDATA[<p>Requirements:</p>
<ul>
<li>an existing Google Cloud Platform account and project</li>
<li>a Java or alternative language web application</li>
<li>a Gradle build that creates a standalone executable JAR file</li>
</ul>
<p>In youd <code>build.gradle</code> file, add the App Engine gradle plugin to your <code>buildscript</code> dependencies:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>buildscript <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    repositories <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        jcenter<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>        mavenCentral<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    dependencies <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        classpath <span style="color:#4070a0">&#39;com.google.cloud.tools:appengine-gradle-plugin:2.+&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Apply the plugin, to make use of it:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>apply <span style="color:#002070;font-weight:bold">plugin:</span> <span style="color:#4070a0">&#34;com.google.cloud.tools.appengine-appyaml&#34;</span>
</span></span></code></pre></div><p>Then you can configure the <code>appengine</code> task to point at the standalone executable JAR:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>appengine <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    stage<span style="color:#666">.</span><span style="color:#4070a0">artifact</span> <span style="color:#666">=</span> 
</span></span><span style="display:flex;"><span>            <span style="color:#4070a0">&#34;${buildDir}/libs/${project.name}-${project.version}.jar&#34;</span>
</span></span><span style="display:flex;"><span>    deploy <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        projectId <span style="color:#666">=</span> <span style="color:#4070a0">&#34;YOUR-PROJECT-ID&#34;</span>
</span></span><span style="display:flex;"><span>        version <span style="color:#666">=</span> <span style="color:#4070a0">&#34;1&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>You can customize the path of the artifact, specify the project ID outside, or define an App Engine version that&rsquo;s dependent on your project version, a git commit, etc.</p>
<p>Note that the App Engine gradle plugin expects to find the <code>app.yaml</code> configuration file in <code>src/main/appengine</code>.</p>
<p>You can then deploy your application with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ ./gradlew appengineDeploy
</span></span></code></pre></div><p>More information:</p>
<ul>
<li><a href="https://cloud.google.com/appengine/docs/standard/java11/config/appref">app.yaml</a></li>
<li><a href="https://github.com/GoogleCloudPlatform/app-gradle-plugin">App Engine gradle plugin</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Implementing Webhooks, not as trivial as it may seem</title><link>https://glaforge.dev/talks/2019/11/25/implementing-webhooks-not-as-trivial-as-it-may-seem/</link><pubDate>Mon, 25 Nov 2019 16:58:28 +0100</pubDate><guid>https://glaforge.dev/talks/2019/11/25/implementing-webhooks-not-as-trivial-as-it-may-seem/</guid><description>&lt;p>You&amp;rsquo;ve certainly interacted with &lt;strong>webhooks&lt;/strong> at some point: with a Github commit webhook, for Slack or Dialogflow chatbots, for being notified of Stripe payments, or when you receive an SMS via Twilio. The concept is fairly well known, but there are some roadblocks along the way, whether you implement a webhook handler (the URL being called) or a webhook backend (the service notifying URLs). It&amp;rsquo;s not necessarily as trivial as it may first seem. As I&amp;rsquo;ve been interested in Web APIs for a long time, I decided to look into this topic a bit more, by working on a new talk.&lt;/p></description><content:encoded>
<![CDATA[<p>You&rsquo;ve certainly interacted with <strong>webhooks</strong> at some point: with a Github commit webhook, for Slack or Dialogflow chatbots, for being notified of Stripe payments, or when you receive an SMS via Twilio. The concept is fairly well known, but there are some roadblocks along the way, whether you implement a webhook handler (the URL being called) or a webhook backend (the service notifying URLs). It&rsquo;s not necessarily as trivial as it may first seem. As I&rsquo;ve been interested in Web APIs for a long time, I decided to look into this topic a bit more, by working on a new talk.</p>
<h2 id="videos">Videos</h2>
<p>I&rsquo;ve had the chance of giving this talk at GeeCON Prague:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/hRz38zGPSAU?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>As well as (in French) at BDX.IO:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/DRf7-dmhNHA?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>You can also watch the slide deck here:</p>
<script async class="speakerdeck-embed" data-id="b89328ff810c4955a8c2427d05f18bed" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<h2 id="summary">Summary</h2>
<p>Initially, I was focusing on the backend webhook implementation aspects, but both the sending and receiving ends of webhooks have their own challenges.</p>
<p>Let me name the ones I encountered.</p>
<p>On the handler / client / receiving side, your webhook handlers should:</p>
<ul>
<li>Reply with a 200 HTTP status code, to let the service provider know that you successfully received the event notification.</li>
<li>Reply fast, so that the service provider doesn&rsquo;t have to keep as many open connections to the handlers, to let it scale more gracefully to more customers. So a good approach is to acknowledge the reception of the event, but treat that event asynchronously afterwards.</li>
<li>Ack reception and defer event handling, as mentioned above when replying fast, it&rsquo;s important to &ldquo;ack&rdquo; quickly the reception, and then you&rsquo;re free to do long event handling afterwards, potentially with some worker queue, with workers that can treat those events at their own pace. You can then scale your workers pool when you need to deal with more notifications.</li>
<li>Calls should be idempotent. Sometimes, for various reasons, it&rsquo;s possible you get event notifications twice for the same event.</li>
<li>Use IP whitelisting, when possible, to ensure that only some IP addresses can ping your handler. Since you&rsquo;re opening an URL to the public, better be sure that it&rsquo;s only the service provider that calls you. But it&rsquo;s not always possible to define such a whitelist, as IP addresses are not necessarily fixed for the service provider.</li>
<li>Check request signature, this time not to avoid a DDoS, but more to ensure the integrity of the event payload that you receive. More on signatures in the server part below.</li>
<li>Take advantage of serverless solutions, as sometimes, you don&rsquo;t get a frequent or regular flow of event notifications, why have a server running all the time? Instead, you can take advantage of serverless solutions, like <a href="https://cloud.google.com/appengine/">App Engine</a> or <a href="https://cloud.google.com/run/">Cloud Run</a>, as you&rsquo;re only billed for the time used.</li>
</ul>
<p>On the service provider / server / notifier side, your webhook backend should:</p>
<ul>
<li>Send small data payloads, instead of the whole resource that triggered the event. For instance, your service might notified handlers that there&rsquo;s a new video available. But you don&rsquo;t want to send tons of gigabytes of videos to each and every handler subscribed to that event. So just send a reference to that resource, and keep the event payload small.</li>
<li>Timeout if client is too slow, as you can&rsquo;t wait forever for a faulty handler to reply. Cut the connection if the client handler doesn&rsquo;t reply under a set time interval, and treat this as if the message hasn&rsquo;t been successfully delivered. Which means you&rsquo;ll have to retry sending that event later on.</li>
<li>Retry sending events with exponential backoff, to not overload a handler which is struggling to keep pace, in order to avoid DDoS-ing it with your retries. Instead, use exponential backoff to try to redeliver, for example, after 1s, 2s, 4s, 8s, 16s, etc. </li>
<li>Keep track of non-responding handlers, after too many failed delivery attempts, mark those handlers as non-responding, and perhaps somehow notify the creator of the handler that it&rsquo;s not responding correctly.</li>
<li>Deliver messages from a work queue, as you have potentially tons of subscribers interested in your events, you don&rsquo;t want your event loop to be taking longer and longer to operate as the number of handlers grow, and instead, offload the work to some worker queue that you can scale independently from the work of keeping pace with ongoing events flow.</li>
<li>Batch events when too frequent, when there are too many event notifications to send. It might be more costly to send each and every event as they come, in real-time. If there are too many events, you can group them in one batch, so as to deliver them at an increased interval of time to your handlers.</li>
<li>Use a dead letter queue, for auditing purpose in particular. For non-responding handlers, or in case of handlers sometimes miss some events, you can push those never-received events in a dead letter queue, so that later on handler developers can check it to see if they actually missed something at some point in the flow.</li>
<li>Use HTTPS for secured connections, well, everyone should use HTTPS all the time these days anyone, but it&rsquo;s better for avoiding man-in-the-middle attacks, to avoid events replay, etc.</li>
<li>Sign requests with a secret, when handlers and service providers share a common secret, the provider can signe the request it sends to handlers, so that handlers can check the message is really coming from the service provider. For example, the Github API is using an HMAC signature, with a SHA-1 digest.</li>
<li>Use proper authentication/authorization mechanisms. This one is a bit vague, but the usual authentication/authorization best practices still apply to webhooks!</li>
</ul>
<p>Going further, I&rsquo;d like to expand this presentation with more hands-on concrete demos, that put all those best practices into action, and perhaps create some animations to show what happens when handlers are flooded with notifications, when handlers don&rsquo;t respond rapidly enough, etc, as that would probably help visualise more concretely each of those problems or practices. Let&rsquo;s see how I can continue iterating and improving this presentation and topic!</p>
<p>Resources</p>
<p>Last but not least, there are some great resources available on the topic, that I&rsquo;ve added at the end of my slide deck. Be sure to check them out as well:</p>
<ul>
<li><a href="https://speakerdeck.com/apistrat/crafting-a-great-webhooks-experience-by-john-sheehan">Crafting a great webhooks experience</a> (John Sheehan)</li>
<li><a href="https://requestbin.com/blog/working-with-webhooks/">WebHooks: the definitive guide</a></li>
<li><a href="https://www.infoq.com/presentations/webhooks-api/">WebHooks: The API Strikes Back</a> (InfoQ)</li>
<li><a href="https://hackernoon.com/webhook-vs-api-whats-the-difference-8d41e6661652">Webhooks vs APIs</a></li>
<li><a href="https://www.programmableweb.com/news/what-webhooks-push-styled-api-and-how-does-it-work/analysis/The2017/03/28">What is a Webhooks push-style API &amp; how does it work</a> (ProgrammableWeb)</li>
<li><a href="https://restful.io/webhooks-dos-and-dont-s-what-we-learned-after-integrating-100-apis-d567405a3671">Webhooks do&rsquo;s &amp; dont&rsquo;s: what we learned after integration 100+ APIs</a></li>
<li><a href="https://www.programmableweb.com/news/what-are-webhooks-and-how-do-they-enable-real-time-web/2012/01/30">How &amp; why Pusher adopted Webhooks</a></li>
<li><a href="https://nordicapis.com/webhooks-vs-websub-which-one-is-better-to-stream-your-events-in-real-time/">Webhooks vs WebSub: Which Is Better For Real-Time Event Streaming?</a></li>
<li><a href="https://techblog.commercetools.com/webhooks-the-devil-in-the-details-ca7f7982c24f">Webhooks, the devil is in the details</a></li>
<li><a href="https://phalt.github.io/webhooks-in-apis/">How to design a webhook for my API</a></li>
<li><a href="https://tomasz.janczuk.org/2018/03/serverless-webhooks-to-revolutionize-the-saas.html">Serverless webhooks to revolutionize the SaaS</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>App Engine 2nd generation runtimes and serverless containers with Cloud Run at Cloud Next Tokyo</title><link>https://glaforge.dev/talks/2019/08/08/app-engine-2nd-generation-runtimes-and-serverless-containers-with-cloud-run-at-cloud-next-tokyo/</link><pubDate>Thu, 08 Aug 2019 17:04:06 +0100</pubDate><guid>https://glaforge.dev/talks/2019/08/08/app-engine-2nd-generation-runtimes-and-serverless-containers-with-cloud-run-at-cloud-next-tokyo/</guid><description>&lt;p>Last week, I was in Tokyo for the first time, to speak at the Google Cloud Next conference. During the DevDay, I spoke about Google App Engine and its 2nd generation runtimes, and I also presented Cloud Run on how to deploy and run containers in a serverless fashion. It&amp;rsquo;s been awesome to visit Japan for the first time and get a chance to meet developers there. Here are the slides I presented:&lt;/p></description><content:encoded>
<![CDATA[<p>Last week, I was in Tokyo for the first time, to speak at the Google Cloud Next conference. During the DevDay, I spoke about Google App Engine and its 2nd generation runtimes, and I also presented Cloud Run on how to deploy and run containers in a serverless fashion. It&rsquo;s been awesome to visit Japan for the first time and get a chance to meet developers there. Here are the slides I presented:</p>
<h2 id="app-engine-2nd-generation-runtimes">App Engine 2nd generation runtimes</h2>
<script async class="speakerdeck-embed" data-id="675bbf764343407d92eb4c2d9618de5c" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<h2 id="serverless-containers-with-cloud-run">Serverless containers with Cloud Run</h2>
<script async class="speakerdeck-embed" data-id="59137c528fc04162a7806605fd936b4e" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Getting started with Micronaut on Google App Engine Java 11</title><link>https://glaforge.dev/posts/2019/07/04/getting-started-with-micronaut-on-google-app-engine-java-11/</link><pubDate>Thu, 04 Jul 2019 17:07:02 +0100</pubDate><guid>https://glaforge.dev/posts/2019/07/04/getting-started-with-micronaut-on-google-app-engine-java-11/</guid><description>&lt;p>A &lt;a href="https://cloud.google.com/blog/products/application-development/turn-it-up-to-eleven-java-11-runtime-comes-to-app-engine">new Java runtime was announced for Google App Engine&lt;/a> standard: with Java 11. It&amp;rsquo;s currently in beta, but anybody can already try it out. Another interesting announcement was the fact that the instances running your apps now get &lt;a href="https://cloud.google.com/blog/products/application-development/app-engine-second-generation-runtimes-now-get-double-the-memory-plus-go-112-and-php-73-now-generally-available">double the memory&lt;/a>! So with this double dose of great news, I decided to craft a little tutorial to show how to deploy a Micronaut application on App Engine Java 11. And because Apache Groovy is, well, groovy, I&amp;rsquo;ll go ahead and use Groovy for my programming language, but of course, the same steps apply to Java workloads as well.&lt;/p></description><content:encoded>
<![CDATA[<p>A <a href="https://cloud.google.com/blog/products/application-development/turn-it-up-to-eleven-java-11-runtime-comes-to-app-engine">new Java runtime was announced for Google App Engine</a> standard: with Java 11. It&rsquo;s currently in beta, but anybody can already try it out. Another interesting announcement was the fact that the instances running your apps now get <a href="https://cloud.google.com/blog/products/application-development/app-engine-second-generation-runtimes-now-get-double-the-memory-plus-go-112-and-php-73-now-generally-available">double the memory</a>! So with this double dose of great news, I decided to craft a little tutorial to show how to deploy a Micronaut application on App Engine Java 11. And because Apache Groovy is, well, groovy, I&rsquo;ll go ahead and use Groovy for my programming language, but of course, the same steps apply to Java workloads as well.</p>
<h2 id="getting-started-on-google-cloud-platform">Getting started on Google Cloud Platform</h2>
<p>In this article, I assume you&rsquo;ve created an account on Google Cloud Platform already (follow the &ldquo;getting started&rdquo; blue buttons to create an account otherwise and benefit from the <a href="https://cloud.google.com/free/">free tier and free quota</a>), and that you&rsquo;ve downloaded and installed the <a href="https://cloud.google.com/sdk/gcloud/">gcloud</a> command-line SDK. You&rsquo;ll be able to follow the first few steps in the <a href="https://cloud.google.com/appengine/docs/standard/java11/quickstart">quickstart guide</a>, to create your GCP project and make it ready for using App Engine, in particular those commands:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ gcloud projects create mn113-gae-java11 --set-as-default
</span></span></code></pre></div><p>You&rsquo;ll have to change the project ID from <code>&quot;mn113-gae-java11&quot;</code> to your own name of choice.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ gcloud app create --project<span style="color:#666">=</span>mn113-gae-java11
</span></span></code></pre></div><p>It&rsquo;ll ask for a cloud region to use, I&rsquo;ve decided to go with europe-west for this one.</p>
<p>The above steps can as well be done from the cloud console UI as well, at <a href="https://console.cloud.google.com/">https://console.cloud.google.com</a>.</p>
<p>Although your application will run for free within the free quota, we need to enable billing for our app, as it&rsquo;s going to use Cloud Build to build our app, and the latter requires billing to be enabled.</p>
<p>To enable billing and the Cloud Build API, please follow the first step of the <a href="https://cloud.google.com/appengine/docs/standard/java11/quickstart">quickstart guide</a> mentioned above.</p>
<h2 id="building-our-micronaut-application">Building our Micronaut application</h2>
<p>Time to fire the Micronaut goodness! On my machine, I&rsquo;m using SDKman to install my SDKs, so I&rsquo;ve installed Java 11 and Micronaut 1.1.3 as explained in <a href="https://docs.micronaut.io/latest/guide/index.html#buildCLI">Micronaut&rsquo;s getting started guide</a>.</p>
<p>Our first step will be to create our basic Micronaut application, thanks to the following command, with the mn command-line SDK:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ mn create-app mn113-gae-java11 --lang<span style="color:#666">=</span>groovy
</span></span></code></pre></div><p>The structure of your Micronaut project is created, with a Gradle-based build, an Application main class, an <code>application.yml</code> file to configure your application.</p>
<p>As this application isn&rsquo;t yet doing anything useful, we&rsquo;re create a <code>&quot;Hello World&quot;</code> controller with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ mn create-controller hello
</span></span></code></pre></div><p>We&rsquo;ll modify this newly created HelloController.groovy controller as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">package</span> mn113<span style="color:#666">.</span><span style="color:#4070a0">gae</span><span style="color:#666">.</span><span style="color:#4070a0">java11</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">io.micronaut.http.annotation.*</span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Controller</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;/hello&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">HelloController</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Get</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;/&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    String <span style="color:#06287e">index</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">return</span> <span style="color:#4070a0">&#34;Hello Micronaut!&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>|</p>
<p>On the <code>/hello</code> path, we&rsquo;ll simply return a plain text response showing our greeting message.</p>
<p>To run your application locally, to check everything is working fine, you&rsquo;ll simply run:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ ./gradlew run
</span></span></code></pre></div><p>And you can check that <code>localhost:8080/hello</code> returns the <code>Hello Micronaut</code> message. So far so good.</p>
<h2 id="configure-our-micronaut-application-for-app-engine">Configure our Micronaut application for App Engine</h2>
<p>In order to deploy the App Engine, we&rsquo;ll use the App Engine Gradle plugin. So we need to amend our <code>build.gradle</code> a little.</p>
<p>Let&rsquo;s define where Gradle will find the plugin:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>buildscript <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    repositories <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        jcenter<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>        mavenCentral<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    dependencies <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        classpath <span style="color:#4070a0">&#39;com.google.cloud.tools:appengine-gradle-plugin:2.+&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>We&rsquo;ll make use of the plugin:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>apply <span style="color:#002070;font-weight:bold">plugin:</span> <span style="color:#4070a0">&#34;com.google.cloud.tools.appengine-appyaml&#34;</span>
</span></span></code></pre></div><p>Let&rsquo;s configure the App Engine section:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>appengine <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    stage<span style="color:#666">.</span><span style="color:#4070a0">artifact</span> <span style="color:#666">=</span>
</span></span><span style="display:flex;"><span>            <span style="color:#4070a0">&#34;${buildDir}/libs/${project.name}-${project.version}.jar&#34;</span>
</span></span><span style="display:flex;"><span>    deploy <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        projectId <span style="color:#666">=</span> <span style="color:#4070a0">&#34;mn113-gae-java11&#34;</span>
</span></span><span style="display:flex;"><span>        version <span style="color:#666">=</span> <span style="color:#4070a0">&#34;1&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Note that App Engine&rsquo;s version string is not supporting dots or underscores (only alphanumeric characters), hence why I replaced the <code>version</code> property. Furthermore a reported <a href="https://github.com/GoogleCloudPlatform/app-gradle-plugin/issues/353">issue</a> prevents me from reusing the Gradle project&rsquo;s own project property in the <code>projectId</code> property.</p>
<h2 id="configure-the-app-engine-deployment">Configure the App Engine deployment</h2>
<p>App Engine has its own deployment configuration file, where you will define the App Engine runtime (in our case Java 11), and you can also decide what kind of instance will be used to run your code. Last but not least, you can customize the entry point which defines how your application should be started.</p>
<p>In <code>src/main/appengine</code> we&rsquo;ll add a file named app.yaml:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">runtime</span>:<span style="color:#bbb"> </span>java11<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">instance_class</span>:<span style="color:#bbb"> </span>F4<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">entrypoint</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#39;java -agentpath:/opt/cdbg/cdbg_java_agent.so=--log_dir=/var/log -jar mn113-gae-java11-0.1.jar&#39;</span><span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="deploying-to-app-engine">Deploying to App Engine</h2>
<p>Now you&rsquo;re ready to deploy your Micronaut application on App Engine&rsquo;s Java 11 runtime!</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ ./gradlew appengineDeploy
</span></span></code></pre></div><p>After a minute or so, and if billing and the Cloud Build API are enabled as said in the introduction, your Micronaut app should be deployed! You can then browse <a href="https://mn113-gae-java11.appspot.com/hello">https://mn113-gae-java11.appspot.com/hello</a> and get your Hello Micronaut greeting.</p>
<h2 id="whats-next">What&rsquo;s next</h2>
<p>In upcoming articles, I&rsquo;ll cover some other aspects, like how to configure and optimize static asset serving, or perhaps how to integrate with databases or other services of Google Cloud Platform. So stay tuned!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Update on the recent serverless developments on GCP at DataXDay 2019</title><link>https://glaforge.dev/talks/2019/07/02/update-on-the-recent-serverless-developments-on-gcp-at-dataxday-2019/</link><pubDate>Tue, 02 Jul 2019 17:19:51 +0100</pubDate><guid>https://glaforge.dev/talks/2019/07/02/update-on-the-recent-serverless-developments-on-gcp-at-dataxday-2019/</guid><description>&lt;p>At &lt;a href="https://dataxday.fr/">DataXDay 2019&lt;/a>, last week, I had the chance to present an updated version of my introductory talk on the &lt;a href="https://cloud.google.com/serverless/">serverless compute options&lt;/a> on Google Cloud Platform. There&amp;rsquo;s always something new to cover!&lt;/p>
&lt;p>For instance, if I put my Java Champion hat on, I&amp;rsquo;d like to mention that there are new runtimes for App Engine standard, like the beta for Java 11, and there&amp;rsquo;s twice the amount of memory as before. On Cloud Functions, we have an alpha for Java as well (currently Java 8, but it&amp;rsquo;ll be soon moved to Java 11 instead, as customers are more interested in the latest LTS version)&lt;/p></description><content:encoded>
<![CDATA[<p>At <a href="https://dataxday.fr/">DataXDay 2019</a>, last week, I had the chance to present an updated version of my introductory talk on the <a href="https://cloud.google.com/serverless/">serverless compute options</a> on Google Cloud Platform. There&rsquo;s always something new to cover!</p>
<p>For instance, if I put my Java Champion hat on, I&rsquo;d like to mention that there are new runtimes for App Engine standard, like the beta for Java 11, and there&rsquo;s twice the amount of memory as before. On Cloud Functions, we have an alpha for Java as well (currently Java 8, but it&rsquo;ll be soon moved to Java 11 instead, as customers are more interested in the latest LTS version)</p>
<p>In this talk, I also covered <a href="http://cloud.run/">Cloud Run</a>, and Cloud Run on GKE (Google Kubernetes Engine), as well as telling a few words about the <a href="https://knative.dev/">Knative</a> open source building blocks for Kubernetes, which allows to create serverless portable containers.</p>
<p>Here&rsquo;s the slide deck I presented at the conference:</p>
<script async class="speakerdeck-embed" data-id="620a008ef3694e93b3a43d15583ff980" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Turn it up to eleven: Java 11 runtime comes to App Engine</title><link>https://glaforge.dev/posts/2019/06/21/turn-it-up-to-eleven-java-11-runtime-comes-to-app-engine/</link><pubDate>Fri, 21 Jun 2019 19:00:26 +0100</pubDate><guid>https://glaforge.dev/posts/2019/06/21/turn-it-up-to-eleven-java-11-runtime-comes-to-app-engine/</guid><description>&lt;p>Yesterday, we &lt;a href="https://cloud.google.com/blog/products/application-development/app-engine-second-generation-runtimes-now-get-double-the-memory-plus-go-112-and-php-73-now-generally-available">announced&lt;/a> new second-generation runtimes for Go 1.12 and PHP 7.3. In addition, App Engine standard instances now run with double the memory. Today, we&amp;rsquo;re happy to announce the availability of the new Java 11 second-generation runtime for App Engine standard in beta. Now, you can take advantage of the latest &lt;a href="https://www.oracle.com/technetwork/java/java-se-support-roadmap.html">Long-Term-Support version&lt;/a> of the Java programming language to develop and deploy your applications on our fully-managed serverless application platform.&lt;/p>
&lt;p>Based on technology from the &lt;a href="https://github.com/google/gvisor">gVisor container sandbox&lt;/a>, second-generation runtimes let you write portable web apps and microservices that take advantage of App Engine&amp;rsquo;s unique auto-scaling, built-in security and pay-per-use billing model&amp;mdash;without some of App Engine&amp;rsquo;s earlier runtime restrictions. Second generation-runtimes also let you build applications more idiomatically. You&amp;rsquo;re free to use whichever framework or library you need for your project&amp;mdash;there are no limitations in terms of what classes you can use, for instance. You can even use native dependencies if needed. Beyond Java, you can also use alternative JVM (Java Virtual Machine) languages like &lt;a href="http://groovy-lang.org/">Apache Groovy&lt;/a>, &lt;a href="https://kotlinlang.org/">Kotlin&lt;/a> or &lt;a href="https://www.scala-lang.org/">Scala&lt;/a> if you wish.&lt;/p></description><content:encoded>
<![CDATA[<p>Yesterday, we <a href="https://cloud.google.com/blog/products/application-development/app-engine-second-generation-runtimes-now-get-double-the-memory-plus-go-112-and-php-73-now-generally-available">announced</a> new second-generation runtimes for Go 1.12 and PHP 7.3. In addition, App Engine standard instances now run with double the memory. Today, we&rsquo;re happy to announce the availability of the new Java 11 second-generation runtime for App Engine standard in beta. Now, you can take advantage of the latest <a href="https://www.oracle.com/technetwork/java/java-se-support-roadmap.html">Long-Term-Support version</a> of the Java programming language to develop and deploy your applications on our fully-managed serverless application platform.</p>
<p>Based on technology from the <a href="https://github.com/google/gvisor">gVisor container sandbox</a>, second-generation runtimes let you write portable web apps and microservices that take advantage of App Engine&rsquo;s unique auto-scaling, built-in security and pay-per-use billing model&mdash;without some of App Engine&rsquo;s earlier runtime restrictions. Second generation-runtimes also let you build applications more idiomatically. You&rsquo;re free to use whichever framework or library you need for your project&mdash;there are no limitations in terms of what classes you can use, for instance. You can even use native dependencies if needed. Beyond Java, you can also use alternative JVM (Java Virtual Machine) languages like <a href="http://groovy-lang.org/">Apache Groovy</a>, <a href="https://kotlinlang.org/">Kotlin</a> or <a href="https://www.scala-lang.org/">Scala</a> if you wish.</p>
<p>In addition to more developer freedom, you also get all the benefits of a serverless approach. App Engine can transparently scale your app up to n and back down to 0, so your application can handle the load when it&rsquo;s featured on primetime TV or goes viral on social networks. Likewise, it scales to zero if no traffic comes. Your bill will also be proportional to your usage, so if nobody uses your app, you won&rsquo;t pay a dime (there is also a free tier available).</p>
<p>App Engine second-generation runtimes also mean you don&rsquo;t need to worry about security tasks like applying OS security patches and updates. Your code runs securely in a <a href="https://gvisor.dev/">gVisor</a>-based sandbox, and we update the underlying layers for you. No need to provision or manage servers yourself&mdash;just focus on your code and your ideas!</p>
<h2 id="whats-new">What&rsquo;s new?</h2>
<p>When you migrate to Java 11, you gain access to all the goodies of the most recent Java versions: you can now use advanced type inference with the new var keyword, create lists or maps easily and concisely with the new immutable collections, and simplify calling remote hosts thanks to the graduated HttpClient support. Last but not least, you can also use the JPMS module system introduced in Java 9.</p>
<p>You&rsquo;ll also find some changes in the Java 11 runtime. For example, the Java 11 runtime does not provide a Servlet-based runtime anymore. Instead, you need to bundle a server with your application in the form of an executable JAR. This means that you are free to choose whichever library or framework you want, be it based on the Servlet API or other networking stacks like the Netty library. In other words, feel free to use <a href="https://spring.io/projects/spring-boot">Spring Boot</a>, <a href="https://vertx.io/">Vert.x</a>, <a href="http://sparkjava.com/">SparkJava</a>, <a href="https://ktor.io/">Ktor</a>, <a href="https://helidon.io/#/">Helidon</a> or <a href="https://micronaut.io/">Micronaut</a> if you wish!</p>
<p>Last but not least, second-generation runtimes don&rsquo;t come with the built-in APIs like Datastore or memcache from the App Engine SDK. Instead, you can use the standalone services with their Google Cloud client libraries, or use other similar services of your choice. Be sure to look into our <a href="https://cloud.google.com/appengine/docs/standard/java11/java-differences">migration guide</a> for more help on these moves.</p>
<h2 id="getting-started">Getting started</h2>
<p>To deploy to App Engine Java 11, all you need is an app.yaml file where you specify <code>runtime: java11</code>, signifying that your application should use Java 11. That&rsquo;s enough to tell App Engine to use the Java 11 runtime, regardless of whether you&rsquo;re using an executable JAR, or a WAR file with a provided servlet-container. However, the new runtime also gives you more control on how your application starts: by specifying an extra <code>entrypoint</code> parameter in <code>app.yaml</code>, you can then customize the java command flags, like the -X memory settings.</p>
<p>With Java 11, the <code>java</code> command now includes the ability to run single independent <code>*.java</code> files without compiling them with <code>javac</code>! For this short getting started section, we are going to use it to run the simplest hello world example with the JDK&rsquo;s built-in HTTP server:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">com.sun.net.httpserver.HttpServer</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.io.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.net.InetSocketAddress</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Main</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">main</span>(String<span style="color:#666">[]</span><span style="color:#bbb"> </span>args)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">throws</span><span style="color:#bbb"> </span>IOException<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>server<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>HttpServer.<span style="color:#4070a0">create</span>(<span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>InetSocketAddress(8080),<span style="color:#bbb"> </span>0);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>server.<span style="color:#4070a0">createContext</span>(<span style="color:#4070a0">&#34;/&#34;</span>,<span style="color:#bbb"> </span>t<span style="color:#bbb"> </span><span style="color:#666">-&gt;</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>response<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Hello World from Java 11.&#34;</span>.<span style="color:#4070a0">getBytes</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>t.<span style="color:#4070a0">sendResponseHeaders</span>(200,<span style="color:#bbb"> </span>response.<span style="color:#4070a0">length</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">try</span><span style="color:#bbb"> </span>(<span style="color:#007020;font-weight:bold">var</span><span style="color:#bbb"> </span>os<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>t.<span style="color:#4070a0">getResponseBody</span>())<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                </span>os.<span style="color:#4070a0">write</span>(response);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>});<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>server.<span style="color:#4070a0">start</span>();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Notice how our Main class uses the var keyword introduced in Java 10, and how we re-used the keyword again in the <code>try-</code>with-resources block, as <code>Java 11</code> makes possible.</p>
<p>Now it&rsquo;s time to prepare our <code>app.yaml</code> file. First, specify the <code>java11</code> runtime. In addition, <code>entrypoint</code> define the actual <code>java</code> command with which to we&rsquo;ll be running to launch the server. The <code>java</code> command points at our single Java source file:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">runtime</span>:<span style="color:#bbb"> </span>java11<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">entrypoint</span>:<span style="color:#bbb"> </span>java Main.java<span style="color:#bbb">
</span></span></span></code></pre></div><p>Finally, don&rsquo;t forget to deploy your application with the <code>gcloud app deploy app.yaml</code> command. Of course, you can also take advantage of dedicated Maven and Gradle plugins for your deployments.</p>
<h2 id="try-java-11-on-app-engine-standard-today">Try Java 11 on App Engine standard today</h2>
<p>You can write your App Engine applications with Java 11 today, thanks to the newly released runtime in beta. Please read the documentation to get started and learn more about it, have a look at the <a href="https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/appengine-java11">many samples</a> that are available, and check out the <a href="https://cloud.google.com/appengine/docs/standard/java11/java-differences">migration guide</a> on moving from Java 8 to 11. And don&rsquo;t forget you can take advantage of the <a href="https://cloud.google.com/free/docs/always-free-usage-limits">App Engine free tier</a> while you experiment with our platform.</p>
<p><em>From the App Engine Java 11 team Ludovic Champenois, Eamonn McManus, Ray Tsang, Alexis Moussine-Pouchkine, Averi Kitsch, Lawrence Latif, Angela Funk.</em></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A serverless Java developer's journey</title><link>https://glaforge.dev/talks/2019/04/17/a-serverless-java-developer-journey/</link><pubDate>Wed, 17 Apr 2019 17:22:02 +0100</pubDate><guid>https://glaforge.dev/talks/2019/04/17/a-serverless-java-developer-journey/</guid><description>&lt;p>Last week at the Google &lt;a href="https://cloud.withgoogle.com/next/sf/">Cloud Next&lt;/a> conference, I had the chance to speak about the Java developer&amp;rsquo;s journey through the &amp;ldquo;serverless&amp;rdquo; offering of &lt;a href="https://cloud.google.com/">Google Cloud Platform&lt;/a>, with my colleague Vinod Ramachandran (Product Manager on some of our serverless products):&lt;/p>
&lt;p>Serverless Java in 2019 is going to be ubiquitous in your favorite cloud. Well, it&amp;rsquo;s actually been 10 years since you could take advantage of Java on Google App Engine. But now you can run your apps on the brand-new Java 11 runtime. Not only servlet-based apps but also executable JARs. And what about authoring functions? Until now, you could only use Node or Python, but today, Java is the third runtime available for Google Cloud Functions. We will review the various ways you can develop your Java functions. Last but not least, thanks to serverless containers, containerized Java workloads run serverlessly, without you caring for infrastructure, scaling, or paying for idle machines. Through various demos, we will look at the many ways Java developers will be able to write, build, test, and deploy code in Java on the rich serverless offering of Google Cloud Platform.&lt;/p></description><content:encoded>
<![CDATA[<p>Last week at the Google <a href="https://cloud.withgoogle.com/next/sf/">Cloud Next</a> conference, I had the chance to speak about the Java developer&rsquo;s journey through the &ldquo;serverless&rdquo; offering of <a href="https://cloud.google.com/">Google Cloud Platform</a>, with my colleague Vinod Ramachandran (Product Manager on some of our serverless products):</p>
<p>Serverless Java in 2019 is going to be ubiquitous in your favorite cloud. Well, it&rsquo;s actually been 10 years since you could take advantage of Java on Google App Engine. But now you can run your apps on the brand-new Java 11 runtime. Not only servlet-based apps but also executable JARs. And what about authoring functions? Until now, you could only use Node or Python, but today, Java is the third runtime available for Google Cloud Functions. We will review the various ways you can develop your Java functions. Last but not least, thanks to serverless containers, containerized Java workloads run serverlessly, without you caring for infrastructure, scaling, or paying for idle machines. Through various demos, we will look at the many ways Java developers will be able to write, build, test, and deploy code in Java on the rich serverless offering of Google Cloud Platform.</p>
<p>Until fairly recently, our compute serverless products consisted only of <a href="https://cloud.google.com/appengine/">Google App Engine</a> for deploying apps and services, and <a href="https://cloud.google.com/functions/">Cloud Functions</a> for deploying functions. Furthermore, for the Java developer, the situation wasn&rsquo;t that great as Cloud Functions wasn&rsquo;t offering any Java support (only Node, Python and Go runtimes), and only App Engine provided a Java 8 runtime.</p>
<p>Fortunately, some very important announcements were made at Cloud Next:</p>
<ul>
<li>
<p>First of all, in addition to the Java 8 runtime, we have launched an alpha for a brand <a href="https://docs.google.com/forms/d/e/1FAIpQLSf5uE5eknJjFEmcVBI6sMitBU0QQ1LX_J7VrA_OTQabo6EEEw/viewform">new App Engine Java 11 runtime</a>.</p>
</li>
<li>
<p>We introduced a <a href="https://docs.google.com/forms/d/e/1FAIpQLScC98jGi7CfG0n3UYlj7Xad8XScvZC8-BBOg7Pk3uSZx_2cdQ/viewform">Java 8 flavor for Cloud Functions</a>.</p>
</li>
<li>
<p>Last but not least, we launched a new product, <a href="https://cloud.google.com/run/">Cloud Run</a>, which allows you to run containers serverlessly, and thus any Java workload that can be containerized.</p>
</li>
</ul>
<p>So you can develop Java functions, Java apps and Java-powered containers in a serverless fashion:</p>
<ul>
<li>Scaling on demand as needed to serve incoming requests as well as down to zero when no traffic comes.</li>
<li>Paying proportionally to the usage.</li>
<li>And all of that, without having to worry with server or cluster provisioning and management.</li>
</ul>
<p>Without further ado, let me share with you the video and the slides of this presentation:</p>
<p>Video recording:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/WnhAYX1Phxw?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>Now it&rsquo;s your turn! If you want to try out functions, apps, and containers in Java, here are a few pointers to get you started:</p>
<ul>
<li><a href="https://docs.google.com/forms/d/e/1FAIpQLSf5uE5eknJjFEmcVBI6sMitBU0QQ1LX_J7VrA_OTQabo6EEEw/viewform">Sign-up form</a> for App Engine Java 11</li>
<li><a href="https://docs.google.com/forms/d/e/1FAIpQLScC98jGi7CfG0n3UYlj7Xad8XScvZC8-BBOg7Pk3uSZx_2cdQ/viewform">Sign-up form</a> for Cloud Functions Java 8</li>
<li>Cloud Run <a href="https://cloud.google.com/run/">documentation</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>On curiosity and sharing with the world</title><link>https://glaforge.dev/posts/2019/02/15/on-curiosity-and-sharing-with-the-world/</link><pubDate>Fri, 15 Feb 2019 17:28:04 +0100</pubDate><guid>https://glaforge.dev/posts/2019/02/15/on-curiosity-and-sharing-with-the-world/</guid><description>&lt;p>At the end of December, I was contacted by someone I didn&amp;rsquo;t know, who asked me some interesting questions, and that led me to quite a bit of introspection.&lt;/p>
&lt;p>As a Java Champion and with your career history. I wanted to ask you what you consider are the most important skills for a Java programmer to have in their toolbox, especially a Senior Java programmer? Or maybe even a better question is what skills you developed that helped you become the Java Developer/Groovy Language Developer that you are today.&lt;/p></description><content:encoded>
<![CDATA[<p>At the end of December, I was contacted by someone I didn&rsquo;t know, who asked me some interesting questions, and that led me to quite a bit of introspection.</p>
<p>As a Java Champion and with your career history. I wanted to ask you what you consider are the most important skills for a Java programmer to have in their toolbox, especially a Senior Java programmer? Or maybe even a better question is what skills you developed that helped you become the Java Developer/Groovy Language Developer that you are today.</p>
<p>In a nutshell, as I answered this person, for me it all boiled down to lots of curiosity, and the desire to share my findings with the world. It&rsquo;s not really about knowing specific methodologies, technologies or languages, or which soft or hard skills to master. It&rsquo;s about the core attitudes from which all the rest will derive from. But first, a bit of background about me.</p>
<h2 id="a-bit-of-history">A bit of history</h2>
<p>Alright, so if I was contacted (and actually a few others as well) with those questions, it&rsquo;s because I&rsquo;m considered to be a visible and public person. Because I&rsquo;m known for my work in the Java community and more precisely in the Apache Groovy ecosystem. I&rsquo;ve been in the field for quite a number of years, along with my contributions in Open Source, and that makes me a senior developer. But how did I get there?</p>
<p>You&rsquo;ve learned a lot during your studies, but often, not much of what you learned is immediately applicable in your daily duties and tasks. So there&rsquo;s even more to learn to become a productive developer. I started working as a junior Java developer in 2001. I was lucky to have had a great mentor that helped me design and write better code. I also spent quite some time reading Java and development related news websites or blogs. I wanted to know what were the latest trends (new language features, frameworks), the best tools for the job, how developers were developing on their projects. So clearly, I was pretty curious to look beyond just what I was doing at work, but to see if I could become a better programmer by learning from others. There&rsquo;s so much great content on the web, so much information that is shared, from best practices to bug fixes explanations, that you can learn a lot. That&rsquo;s also more or less when I started blogging. I saw so many useful blog posts that helped me, that I thought it would be a good thing to share back things I learned that could be helpful to others as well.</p>
<p>In 2003, at work, I needed a way to extend an app I was working on, and clearly, some kind of scripting solution was what would allow the end-users of our app to further customize and tailor the application to their needs. So I spent some time reviewing existing Java scripting solutions, but none were really ideal. Fortunately, that&rsquo;s when Groovy was born. It was embryonic, and not really ready for prime time though. But it was what I needed.</p>
<p>I started playing with Groovy, but quickly I encountered tons of problems and bugs. Since the code was Open Source, I started looking at its codebase, outside of work. I quickly understood where some of the bugs were coming from, and found ways to fix them. Since the community was pretty open, I participated in the mailing-lists to tell about those bugs, to help other users. It was nice to feel being part of a nice, friendly and helpful community.</p>
<p>I used the bug tracker to file bugs and feature requests, and when I could I even submitted some patches to fix these. My patches were accepted, and in a handful of months, I was asked to become an official committer on the project (which I gladly accepted). By working with the other committers, I learned a lot about Java, the JVM, or how open source projects worked. That was super interesting. Since the code was public, I really wanted all my contributions to be top-notch, perfectly well tested and commented. Somehow I had the impression that the scrutiny of my peers mandated that I had to produce even better code than at work! So I perfected my craft. A lot.</p>
<p>Since I had already started sharing my findings on my blog (and later on on social networks), I became part of the so-called &ldquo;blogosphere&rdquo;, and started interacting with other bloggers. I wrote about Java and Groovy, of course, but the discussions with other open source developers, allowed me to also meet them in the real world. We even started a meetup of open source developers that shared what they were working on. That&rsquo;s how I did my first public presentation, to show Groovy to my peers, in 2004 or so, at our local gatherings. I came to know people working for big companies like Sun or Oracle, as well as smaller actors, from freelancers, to entrepreneurs. A handful of those companies started using Groovy, and that&rsquo;s how one day, someone asked if I&rsquo;d be ready to talk with them at a big conference. That was for JavaOne! My first big conference and presentation was in the US in front of 600 persons. Woh&hellip; That&rsquo;s how I started sharing more widely with the world, and also started travelling to spread the word.</p>
<p>I spent a lot of time on Groovy and its ecosystem, and I later got the chance to both work on those technologies for a living (after doing quite a bit of consulting), as well as even creating my own company to focus on the project. At the same time, I was still continuing presenting about Groovy, and still improving the language thanks to the feedback I was getting from the many developers I was meeting all around the world. I was doing developer advocacy at the same time as product management and development. Three hats in one. And by doing developer advocacy, that&rsquo;s also what landed me my current job of developer advocate at Google.</p>
<h2 id="the-ever-changing-nature-of-our-field">The ever changing nature of our field</h2>
<p>From the narrated history above, there&rsquo;s a theme that emerges: curiosity. But what lead me to being curious? Tons of people are doing 9-to-5 jobs, and that&rsquo;s totally fine. However, as we spend so much time in our lives at work, for me, it had to be interesting and motivating. To be interesting, work has to be somehow entertaining &mdash; beside spending quality time with great coworkers. If it&rsquo;s not interesting, you get bored very easily, and you don&rsquo;t want to wake up every morning to go to the office. So how not to be bored? By making your job more interesting. How to make it more interesting? Well, if you&rsquo;re passionate about what you&rsquo;re doing, it&rsquo;s much easier to go through the day and do fun and interesting things, event for a project that could appear as not being very fancy.</p>
<p>Programming was first a hobby, for me, as a child and teenager. It never really occurred to me it could become my job. Initially, I just wanted to be&hellip; an &ldquo;engineer&rdquo;. Perhaps in aerospace, or something like this. Who hasn&rsquo;t dreamt of becoming an astronaut? It&rsquo;s only late in my studies that I thought I could actually become a developer. So my hobby, my passion, became my job. But there&rsquo;s a big difference between working on stuff you want, versus being asked to work on stuff for the company which hired you. In order to not be bored, be sure to push for improving the project in interesting ways both for you and the end-users. If possible, perhaps try to introduce and learn new technologies that can make the product better, and at the same time make you learn something new. Be passionate about improving both your projects and your skills.</p>
<p>Notice also that in our field, we actually don&rsquo;t really have a choice but to learn. When I was a student, my current job didn&rsquo;t even exist. When I started working, the languages or tools I&rsquo;m using today weren&rsquo;t available then yet. So in IT, in programming, etc, there&rsquo;s always a new language, a new tool, a new practice, new patterns, etc, that come to light. It&rsquo;s a field where we have to be in a constant learning state, in order to stay relevant. If you&rsquo;re not learning, your skills will rot, you&rsquo;ll be less employable, you&rsquo;ll diminish your chances of having a fantastic job. So you have to be curious and learn all the time. To not be bored, but also to get better at your craft.</p>
<p>With all those new tools, languages, frameworks, technologies, you have to keep up with what&rsquo;s going on. You have to be ready to learn something new.</p>
<h2 id="sharing-is-caring">Sharing is caring</h2>
<p>We talked a lot about being curious, about learning all along, but I also mentioned about sharing. As the saying goes, sharing is caring, but it&rsquo;s also about creating opportunities for you.</p>
<p>Sharing what I learned or worked on was helpful for others too (who encountered similar problems, for example), but it&rsquo;s also how I came to meet wonderful people along the way. Even mentors and role models. If I hadn&rsquo;t blogged or tweeted, I wouldn&rsquo;t have been able to start making presentations at meetups and conferences. And many of the friends I have today are friends I met along the way, at meetups, conferences, working on open source projects together, and so on.</p>
<p>Without sharing my code, I wouldn&rsquo;t have had the opportunity to meet my future employers and colleagues, as well as the co-founders of my own startup. Sharing is great to be visible, of course, but it&rsquo;s a wonderful way to meet new people from whom you&rsquo;ll learn a lot.</p>
<p>Open source is sharing too. Working on open source projects, nurturing communities and ecosystems around those, further allowed me to meet great people around the world. And it&rsquo;s what lead me to get the jobs at companies I was interested in. It created great professional opportunities.</p>
<h2 id="summary">Summary</h2>
<p>Let&rsquo;s try to wrap up a bit. It&rsquo;s really not about learning a particular tool or technology. It&rsquo;s all about being curious and passionate about your craft, and to share what you&rsquo;ve learned with the world.</p>
<h3 id="be-curious">Be curious!</h3>
<p>It&rsquo;ll make your daily job more interesting. You will learn lots of great new technologies. You&rsquo;ll become a better developer by learning from your peers. You&rsquo;ll improve your craft and expertise. It&rsquo;ll increase your employability. You&rsquo;ll even likely become an expert in your field!</p>
<h3 id="share-with-the-world">Share with the world!</h3>
<p>Write, blog, tweet, present about the things you&rsquo;ve learned at meetups or conferences. You&rsquo;ll learn a lot from others along the way. Write and share code, and/or contribute to open source projects. You&rsquo;ll meet awesome peers and mentors. And will create all sorts of interesting job opportunities.</p>
<h3 id="be-curious-and-share-with-the-world">Be curious and share with the world!</h3>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Tip: Making a Google Cloud Storage bucket or file public</title><link>https://glaforge.dev/posts/2019/02/13/tip-making-a-google-cloud-storage-bucket-or-file-public/</link><pubDate>Wed, 13 Feb 2019 17:48:00 +0100</pubDate><guid>https://glaforge.dev/posts/2019/02/13/tip-making-a-google-cloud-storage-bucket-or-file-public/</guid><description>&lt;p>&lt;a href="https://cloud.google.com/storage/">Google Cloud Storage&lt;/a> is the ideal product to store your object files (binary files, pictures, audio/video assets, and more).&lt;/p>
&lt;p>Until recently, there was an option in the Google cloud console with a checkbox to quickly make a file or bucket public. However, and I would add &amp;ldquo;unfortunately&amp;rdquo;, users tended to inadvertently clicking the checkbox, thus making potentail confidential assets public. So this risky, but easy, option, has been removed to avoid any unwanted data leak.&lt;/p></description><content:encoded>
<![CDATA[<p><a href="https://cloud.google.com/storage/">Google Cloud Storage</a> is the ideal product to store your object files (binary files, pictures, audio/video assets, and more).</p>
<p>Until recently, there was an option in the Google cloud console with a checkbox to quickly make a file or bucket public. However, and I would add &ldquo;unfortunately&rdquo;, users tended to inadvertently clicking the checkbox, thus making potentail confidential assets public. So this risky, but easy, option, has been removed to avoid any unwanted data leak.</p>
<p>However, of course, it&rsquo;s still possible to <a href="https://cloud.google.com/storage/docs/access-control/making-data-public">make buckets or files stored in Cloud Storage public</a>. But you can&rsquo;t do it without paying attention! As I never quite remember how to do that (in spite of the linked documentation easily found with a quick Google search), I decided to highlight with a few screenshots how to achieve that!</p>
<p>I assume you already have or created a GCP project, and you also have a bucket full of assets that you want to make public, because you need to share them on the Web, for your mobile application, etc.</p>
<p>To illustrate this tip, let&rsquo;s have a look at the GCP cloud console:</p>
<p><figure>
  <a href="#img-0f8a2395305df39980b10feb32241c6a">
    <img src="/img/public-bucket/gcs-01-file-browser-small.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-0f8a2395305df39980b10feb32241c6a">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/public-bucket/gcs-01-file-browser-small.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h2 id="making-a-file-public">Making a file public</h2>
<p>First, we&rsquo;ll have a look at making a single file public.</p>
<p>You&rsquo;ll have to click the vertical triple dot icon on the right of the screen, and click on <code>Edit permissions</code>:</p>
<p><figure>
  <a href="#img-2bb5cfa46956844a7ffa786793f249b4">
    <img src="/img/public-bucket/gcs-02-permissions-drop-down-small.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-2bb5cfa46956844a7ffa786793f249b4">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/public-bucket/gcs-02-permissions-drop-down-small.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Once you&rsquo;ve clicked on this option, you&rsquo;ll be given the choice to add new permissions to members or groups of members:</p>
<p><figure>
  <a href="#img-ad5de9be1fc6b90c4955603308e6fb13">
    <img src="/img/public-bucket/gcs-03-permissions-dialog-small.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-ad5de9be1fc6b90c4955603308e6fb13">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/public-bucket/gcs-03-permissions-dialog-small.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>In our case, we want to allow all the users to have read access on that particular file. So I&rsquo;m giving the <code>Group</code> of <code>allUsers</code> the <code>Reader</code> access level. Then, once saved, in the file browser, you should see the following icon warning you the file is now public:</p>
<p><figure>
  <a href="#img-a4fda09774d00e24cafa1f913e7e88ff">
    <img src="/img/public-bucket/gcs-04-public-warning-small.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-a4fda09774d00e24cafa1f913e7e88ff">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/public-bucket/gcs-04-public-warning-small.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h2 id="making-a-bucket-public">Making a bucket public</h2>
<p>Instead of doing this for each individual file, you can also do the same at the bucket level, to give read access to the bucket and all its files in one go.</p>
<p>From the object browser, click on the <code>Permissions</code> tab. You will have to add the <code>allUsers</code> members the <code>Storage Object Viewer</code> role:</p>
<p><figure>
  <a href="#img-94be41107fe2a1cd54451408f9f61ad2">
    <img src="/img/public-bucket/gcs-05-bucket-permissions-small.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-94be41107fe2a1cd54451408f9f61ad2">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/public-bucket/gcs-05-bucket-permissions-small.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Click on the <code>Add members button</code>, type <code>allUsers</code>, select the <code>Storage &gt; Storage Object Viewer</code> option, as follows, and click <code>add</code>:</p>
<p><figure>
  <a href="#img-8babe3065da6498b4474f1ad5fda25e4">
    <img src="/img/public-bucket/gcs-06-add-role-to-bucket-users-small.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-8babe3065da6498b4474f1ad5fda25e4">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/public-bucket/gcs-06-add-role-to-bucket-users-small.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Now if you head back to the file browser, you&rsquo;ll see all the files have the little warning icon telling you the resource is publicly accessible.</p>
<h2 id="for-command-line-gurus">For command-line gurus</h2>
<p>I showed the visual approach from the cloud console&hellip; but there&rsquo;s a one-liner you can use, thanks to the <a href="https://cloud.google.com/storage/docs/gsutil">gsutil</a> command.</p>
<p>For an individual file:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gsutil acl ch -u AllUsers:R gs://<span style="color:#666">[</span>BUCKET_NAME<span style="color:#666">]</span>/<span style="color:#666">[</span>OBJECT_NAME<span style="color:#666">]</span>
</span></span></code></pre></div><p>For a whole bucket:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gsutil iam ch allUsers:objectViewer gs://<span style="color:#666">[</span>BUCKET_NAME<span style="color:#666">]</span>
</span></span></code></pre></div><p>(Where you replace <code>[BUCKET_NAME]</code> with your project name, and <code>[OBJECT_NAME]</code> with the file name)</p>
<h2 id="more">More&hellip;</h2>
<p>There&rsquo;s also a REST API that you can use to handle your buckets and file, as well as different client libraries in different languages that you can use as well.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Mais c'est quoi un Developer Advocate ?</title><link>https://glaforge.dev/posts/2019/02/04/mais-c-est-quoi-un-developer-advocate/</link><pubDate>Mon, 04 Feb 2019 17:59:07 +0100</pubDate><guid>https://glaforge.dev/posts/2019/02/04/mais-c-est-quoi-un-developer-advocate/</guid><description>&lt;p>J&amp;rsquo;ai eu le plaisir d&amp;rsquo;encadrer des stagiaires de 3ème récemment chez Google. Nous accueillons des enfants, neveux, nièces, cousins d&amp;rsquo;employés de Google (donc non :-P, je ne prends pas de stagiaire, pas la peine de demander !!!) pour leur faire découvrir les différents métiers que nous exerçons dans l&amp;rsquo;entreprise. Et il y en a beaucoup !&lt;/p>
&lt;p>L&amp;rsquo;un de mes stagiaires m&amp;rsquo;a interviewé lorsque je décrivais mon travail de &amp;ldquo;Developer Advocate&amp;rdquo;, au sein de Google Cloud. J&amp;rsquo;ai trouvé cette interview intéressante, et je me suis dit que ça valait le coup de la partager avec vous, en Français (si, si, j&amp;rsquo;écris en Français parfois sur ce blog.)&lt;/p></description><content:encoded>
<![CDATA[<p>J&rsquo;ai eu le plaisir d&rsquo;encadrer des stagiaires de 3ème récemment chez Google. Nous accueillons des enfants, neveux, nièces, cousins d&rsquo;employés de Google (donc non :-P, je ne prends pas de stagiaire, pas la peine de demander !!!) pour leur faire découvrir les différents métiers que nous exerçons dans l&rsquo;entreprise. Et il y en a beaucoup !</p>
<p>L&rsquo;un de mes stagiaires m&rsquo;a interviewé lorsque je décrivais mon travail de &ldquo;Developer Advocate&rdquo;, au sein de Google Cloud. J&rsquo;ai trouvé cette interview intéressante, et je me suis dit que ça valait le coup de la partager avec vous, en Français (si, si, j&rsquo;écris en Français parfois sur ce blog.)</p>
<p>L&rsquo;exercice de cet entretien est intéressant parce qu&rsquo;il me permet d&rsquo;essayer d&rsquo;expliquer de manière simple ce que je fais&hellip; et ce n&rsquo;est pas évidant, vu la technicité de cet univers, des produits sur lesquels on travaille (qui ne sont pas à la portée du grand public). Donc voici quelques unes des questions auxquelles j&rsquo;ai eu à répondre. J&rsquo;espère que vous trouverez cela intéressant.</p>
<h2 id="quel-est-ton-métier-">Quel est ton métier ?</h2>
<p>Je suis <strong>Developer Advocate</strong> pour la branche <strong>Google Cloud</strong> de Google.</p>
<p>C&rsquo;est un titre en anglais qui n&rsquo;a pas vraiment d&rsquo;équivalent en Français.</p>
<p><a href="https://cloud.google.com/">Google Cloud</a>, c&rsquo;est la partie de Google qui propose des produits comme par exemple Gmail pour la messagerie électronique, Google Docs pour le traitement de texte ou de tableurs, mais qui offre également aux développeurs et ingénieurs des serveurs sur lesquels les développeurs peuvent héberger leurs applications, des bases de données pour stocker leurs données, ou bien encore des services autour de l&rsquo;intelligence artificielle pour reconnaître ce qu&rsquo;il y a comme objets dans des images.</p>
<h2 id="quelles-sont-les-principales-activités-dans-ce-métier-">Quelles sont les principales activités dans ce métier ?</h2>
<p>Le rôle d&rsquo;un Developer Advocate est d&rsquo;être une sorte de représentant des développeurs (nos utilisateurs et clients qui créent des applications). Nous collectons les retours de ces développeurs pour voir comment améliorer les produits de Google, en écoutant leurs besoins, leurs demandes, les problèmes qu&rsquo;ils rencontrent.</p>
<p>Par ailleurs, une autre grande partie de ce rôle (la partie la plus visible de l&rsquo;extérieur), c&rsquo;est de présenter et promouvoir les produits de Google (Google Cloud dans mon cas). Il s&rsquo;agit alors d&rsquo;écrire des articles, de coder des exemples de programmes utilisant nos technologies, enregistrer des vidéos, préparer des présentations que nous donnons lors de conférences locales ou internationales, puis aussi tirer partie des réseaux sociaux pour faire passer notre message.</p>
<p>Enfin, nous sommes également les utilisateurs &ldquo;zéro&rdquo; de nos futurs nouveaux produits. Nous sommes donc les premiers à tester ces produits, pour voir ce qui fonctionne ou pas, s&rsquo;ils sont faciles à utiliser, s&rsquo;ils ont des bugs, si l&rsquo;on peut trouver des façons de les améliorer ou de les enrichir avant de les sortir publiquement.</p>
<h2 id="travailles-tu-plutôt-seul-ou-en-équipe--pourquoi-">Travailles-tu plutôt seul ou en équipe ? Pourquoi ?</h2>
<p>Je travaille dans une équipe internationale, répartie dans différents pays, avec des bureaux à San Francisco, Seattle, New York, Londres, Paris ou Tokyo. Parfois je travaille effectivement en équipe, à plusieurs sur un même sujet, mais beaucoup de mes activités sont aussi solitaires, comme lorsque l&rsquo;on prépare une démonstration et une présentation pour une conférence.</p>
<h2 id="dois-tu-prendre-des-initiatives--pourquoi-">Dois-tu prendre des initiatives ? Pourquoi ?</h2>
<p>Le poste de Developer Advocate est un travail qui demande beaucoup d&rsquo;autonomie, et donc qui nécessite de prendre soi même beaucoup d&rsquo;initiatives. C&rsquo;est à moi de décider sur quel sujet travailler, quel article écrire, à quelle conférence je souhaite assister.</p>
<p>Parfois, les Developer Advocates ont des demandes venant des chefs de produits qui souhaiteraient que nous représentions leur produit, mais c&rsquo;est généralement par soi même que l&rsquo;on décide sur quoi travailler.</p>
<h2 id="as-tu-des-responsabilités--si-oui-lesquelles--pourquoi-">As-tu des responsabilités ? Si oui lesquelles ? Pourquoi ?</h2>
<p>Je n&rsquo;encadre personne, donc je n&rsquo;ai en tout cas pas de responsabilité managériales. Par contre, je me focalise sur une certaine gamme de produits, et c&rsquo;est de ma responsabilité de représenter et d&rsquo;améliorer ces produits là.</p>
<h2 id="as-tu-des-contacts-avec-des-personnes-autres-que-tes-collègues-de-travail--pourquoi-">As-tu des contacts avec des personnes autres que tes collègues de travail ? Pourquoi ?</h2>
<p>Comme une partie de mon travail consiste à aller présenter les produits de Google Cloud à des conférences, des &ldquo;meetups&rdquo;, des groupes d&rsquo;utilisateurs, je rencontre effectivement beaucoup de monde lors de ces événements. Egalement au travers des réseaux sociaux, j&rsquo;interagis souvent avec des développeurs aux quatre coins du monde. Et j&rsquo;ai également la chance de pouvoir rencontrer nos clients, pour discuter de leurs problèmes, de leurs besoins, mais aussi de leurs réussites.</p>
<h2 id="les-activités-sont-elles-variées-ou-répétitives-">Les activités sont-elles variées ou répétitives ?</h2>
<p>Les activités sont effectivement variées, aussi bien de la programmation (pour préparer des démonstrations des produits), de la communication (pour des présentations), de l&rsquo;écriture (pour les articles, la documentation que nous rédigeons), mais aussi le voyage au travers le monde permets de découvrir de nouveaux horizons et de nouvelles cultures.</p>
<h2 id="dois-tu-organiser-ton-travail-ou-plutôt-exécuter-des-consignes-">Dois-tu organiser ton travail ou plutôt exécuter des consignes ?</h2>
<p>Comme je le disais plus haut, c&rsquo;est un travail qui demande beaucoup d&rsquo;autonomie, donc c&rsquo;est à moi d&rsquo;organiser mon travail, d&rsquo;autant plus que j&rsquo;ai déjà un certain niveau d&rsquo;ancienneté. Il arrive parfois qu&rsquo;on me demande certaines choses effectivement, mais le plus souvent c&rsquo;est à moi de m&rsquo;organiser.</p>
<h2 id="est-ce-que-le-travail-est-fatigant-nerveusement-et-physiquement--pourquoi-">Est-ce que le travail est fatigant (nerveusement et physiquement) ? Pourquoi ?</h2>
<p>Parfois oui. Il y a beaucoup à faire : Google Cloud possède de nombreux produits, qui sont développés rapidement, il y a donc énormément de choses à couvrir et étudier. Ce qui est également fatigant, c&rsquo;est le fait de voyager au travers le monde avec les décalages horaires, les tracas associés au voyage de manière générale.</p>
<p>Outre le voyage et la quantité de travail, il faut travailler régulièrement avec la maison mère qui est sur la côte Ouest des Etats-Unis, avec 9 heures de décalage horaire, ce qui veut dire qu&rsquo;il faut pouvoir faire des conférences téléphoniques avec mes collègues outre-Atlantique après 18 ou 19 heures, voire parfois même à 21 ou 22 heures ! Autre exemple, lorsque l&rsquo;on donne des présentations à des &ldquo;meetups&rdquo;, c&rsquo;est souvent le soir après le travail, donc on peut rentrer parfois très tard chez soi.</p>
<h2 id="est-ce-que-cette-profession-permet-dobtenir-une-promotion--laquelle-">Est-ce que cette profession permet d&rsquo;obtenir une promotion ? Laquelle ?</h2>
<p>Dans ce rôle, il y a différents grades, différents niveaux. Quand on commence dans ce métier, on entrera à un certain niveau, et au fil des années, on pourra progresser et monter de niveau en niveau. L&rsquo;entreprise n&rsquo;attends pas la même chose de ses employés en fonction de leur niveau d&rsquo;ancienneté. Nous avons une sorte d&rsquo;échelle qui décrit ce qui est attendu de chacun à chaque niveau de sa progression hiérarchique.</p>
<h2 id="quels-sont-les-diplômes-requis-">Quels sont les diplômes requis ?</h2>
<p>Il faut généralement un Bac+5 (type mastère, école d&rsquo;ingénieur) en informatique et avoir déjà une certaine expérience professionnelle dans le domaine de l&rsquo;informatique pour bien comprendre la problématique de nos utilisateurs et clients.</p>
<h2 id="quel-est-le-contenu-des-études-">Quel est le contenu des études ?</h2>
<p>Dans mon cas (un DESS en informatique), ce sont des études qui permettent aux étudiants d&rsquo;apprendre comment fonctionnent les ordinateurs, d&rsquo;apprendre des langages de programmation, comment écrire des algorithmes, comment fonctionne les réseaux informatiques ou les bases de données, etc.</p>
<h2 id="est-ce-quil-y-a-dautres-formations-possibles-formation-continue-cours-du-soir-autres-pour-cette-profession-">Est-ce qu&rsquo;il y a d&rsquo;autres formations possibles (formation continue, cours du soir, autres) pour cette profession ?</h2>
<p>Bien qu&rsquo;un diplôme Bac+5 en informatique soit le plus commun, du moment qu&rsquo;on a une bonne expérience professionnelle dans le domaine, il est aussi possible de bifurquer vers cette profession. Mais il n&rsquo;y a à proprement parlé pas de formation spécifique pour le rôle de Developer Advocate à ma connaissance.</p>
<p>On demande souvent aux jeunes &ldquo;qu&rsquo;est-ce que tu voudras faire plus tard ?&rdquo; </p>
<p>Dans mon cas, je voulais être &ldquo;ingénieur&rdquo;, et j&rsquo;aimais bien l&rsquo;informatique, mais ce qui est amusant c&rsquo;est qu&rsquo;à l&rsquo;époque où j&rsquo;ai fait mes études, le métier de &ldquo;Developer Advocate&rdquo; n&rsquo;existait pas vraiment. Donc qui peut dire quels métiers existeront quand vous serez sur le marché du travail ? Il faut savoir s&rsquo;adapter, car de nouveaux métiers voient le jour régulièrement, et il n&rsquo;y aura pas forcément de formation spécialisée pour y parvenir, ou de parcours type.</p>
<h2 id="quelle-sont-les-qualités-requises-">Quelle sont les qualités requises ?</h2>
<p>Outre le fait d&rsquo;être un bon ingénieur en informatique (savoir programmer, etc.), il faut aussi avoir de très bonnes capacités communicationnelles, un esprit de synthèse et de vulgarisation, car nous devons aider nos utilisateurs et clients à comprendre comment se servir de nos produits.</p>
<p>Par ailleurs, la majeure partie de mon travail s&rsquo;effectue en anglais, qui est la langue de la maison mère, mais la langue internationale en informatique et lorsque l&rsquo;on voyage. Donc il faut absolument être bon en Anglais pour ce travail.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Machine learning APIs with Apache Groovy</title><link>https://glaforge.dev/talks/2019/01/18/machine-learning-apis-with-apache-groovy/</link><pubDate>Fri, 18 Jan 2019 18:07:48 +0100</pubDate><guid>https://glaforge.dev/talks/2019/01/18/machine-learning-apis-with-apache-groovy/</guid><description>&lt;p>At &lt;a href="https://gr8conf.eu/">GR8Conf Europe&lt;/a> last year, I &lt;a href="https://2018.gr8conf.eu/talks/610">talked&lt;/a> 
about how to take advantage of the &lt;a href="https://cloud.google.com/products/ai/building-blocks/">Google Cloud machine learning APIs&lt;/a> 
using &lt;a href="http://groovy-lang.org/">Apache Groovy&lt;/a>.&lt;/p>
&lt;p>With Groovy, you can call the Vision API that recognises what&amp;rsquo;s in your pictures, or reads text.&lt;/p>
&lt;p>You can invoke the Natural Language API to understand the structure of your text.&lt;/p>
&lt;p>With the Speech-To-Text API, you can get transcriptions of what&amp;rsquo;s been said in an audio stream, or with Text-To-Spech,
you can also generate human-like voices from your own text.&lt;/p></description><content:encoded>
<![CDATA[<p>At <a href="https://gr8conf.eu/">GR8Conf Europe</a> last year, I <a href="https://2018.gr8conf.eu/talks/610">talked</a> 
about how to take advantage of the <a href="https://cloud.google.com/products/ai/building-blocks/">Google Cloud machine learning APIs</a> 
using <a href="http://groovy-lang.org/">Apache Groovy</a>.</p>
<p>With Groovy, you can call the Vision API that recognises what&rsquo;s in your pictures, or reads text.</p>
<p>You can invoke the Natural Language API to understand the structure of your text.</p>
<p>With the Speech-To-Text API, you can get transcriptions of what&rsquo;s been said in an audio stream, or with Text-To-Spech,
you can also generate human-like voices from your own text.</p>
<p>With those ready-made APIs, no need to be an expert data scientist! Just call an SDK or a REST API, and you&rsquo;re ready to go.</p>
<p>Here&rsquo;s the video of the presentation:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/WKK1iUoB6CE?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>And here are the slides that I showed:</p>
<script async class="speakerdeck-embed" data-id="c2927b99b7b64b9c83a90fe81704a9f2" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>In a <a href="http://glaforge.appspot.com/article/vision-recognition-with-a-groovy-twist">previous article</a>,
I also presented one of the sample that I used with the Vision API.</p>
<p>The <a href="https://www.gr8conf.eu/cfp">Call for Paper</a> for the new edition of GR8Conf Europe is still open,
so don&rsquo;t miss the opportunity to tell the world about all the cool things you&rsquo;ve been doing with Apache Groovy!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Interview InfoQ en Français sur les microservices sur Google Cloud Platform</title><link>https://glaforge.dev/posts/2019/01/11/interview-infoq-en-francais-sur-les-microservices-sur-google-cloud-platform/</link><pubDate>Fri, 11 Jan 2019 22:11:49 +0100</pubDate><guid>https://glaforge.dev/posts/2019/01/11/interview-infoq-en-francais-sur-les-microservices-sur-google-cloud-platform/</guid><description>&lt;p>Une fois n&amp;rsquo;est pas coutume, je vais parler de Google Cloud Platform en français !
Lors de la conférence Voxxed Days Microservices, que j&amp;rsquo;ai 
&lt;a href="https://glaforge.dev/talks/2019/01/03/new-serverless-solutions-on-google-cloud-for-functions-apps-and-containers/">couverte récemment&lt;/a>,
j&amp;rsquo;ai eu l&amp;rsquo;occasion de répondre à une interview pour InfoQ France.&lt;/p>
&lt;p>&lt;a href="https://www.infoq.com/fr/interviews/voxxeddays-microservices-2018-guillaume-laforge">Interview sur les microservices sur Google Cloud Platform&lt;/a>&lt;/p>
&lt;p>Voici la liste des questions auxquelles j&amp;rsquo;ai répondues, et je vous laisserai écouter les réponses sur 
&lt;a href="https://www.infoq.com/fr/interviews/voxxeddays-microservices-2018-guillaume-laforge">InfoQ France&lt;/a> !&lt;/p>
&lt;ul>
&lt;li>Pour ceux qui ne te connaissent pas, peux-tu nous dire qui tu es ?&lt;/li>
&lt;li>Elles sont où les équipes produits ?&lt;/li>
&lt;li>Et les utilisateurs, en France, il y en a beaucoup ?&lt;/li>
&lt;li>Pour les néophytes, les microservices, qu&amp;rsquo;est-ce que c&amp;rsquo;est ?&lt;/li>
&lt;li>C&amp;rsquo;est quoi le &amp;ldquo;nouveau&amp;rdquo; par rapport aux architectures dites distribuées, soa, webservices ?&lt;/li>
&lt;li>On va parler de la platforme cloud de google, où ça en est ?&lt;/li>
&lt;li>C&amp;rsquo;est quoi serverless, le retour du mainframe ?&lt;/li>
&lt;li>Quelles nouveautés ?&lt;/li>
&lt;li>Et Google vis à vis de java ?&lt;/li>
&lt;li>C&amp;rsquo;est quoi les langages que vous poussez le plus ?&lt;/li>
&lt;li>Le futur des microservices ?&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>Une fois n&rsquo;est pas coutume, je vais parler de Google Cloud Platform en français !
Lors de la conférence Voxxed Days Microservices, que j&rsquo;ai 
<a href="https://glaforge.dev/talks/2019/01/03/new-serverless-solutions-on-google-cloud-for-functions-apps-and-containers/">couverte récemment</a>,
j&rsquo;ai eu l&rsquo;occasion de répondre à une interview pour InfoQ France.</p>
<p><a href="https://www.infoq.com/fr/interviews/voxxeddays-microservices-2018-guillaume-laforge">Interview sur les microservices sur Google Cloud Platform</a></p>
<p>Voici la liste des questions auxquelles j&rsquo;ai répondues, et je vous laisserai écouter les réponses sur 
<a href="https://www.infoq.com/fr/interviews/voxxeddays-microservices-2018-guillaume-laforge">InfoQ France</a> !</p>
<ul>
<li>Pour ceux qui ne te connaissent pas, peux-tu nous dire qui tu es ?</li>
<li>Elles sont où les équipes produits ?</li>
<li>Et les utilisateurs, en France, il y en a beaucoup ?</li>
<li>Pour les néophytes, les microservices, qu&rsquo;est-ce que c&rsquo;est ?</li>
<li>C&rsquo;est quoi le &ldquo;nouveau&rdquo; par rapport aux architectures dites distribuées, soa, webservices ?</li>
<li>On va parler de la platforme cloud de google, où ça en est ?</li>
<li>C&rsquo;est quoi serverless, le retour du mainframe ?</li>
<li>Quelles nouveautés ?</li>
<li>Et Google vis à vis de java ?</li>
<li>C&rsquo;est quoi les langages que vous poussez le plus ?</li>
<li>Le futur des microservices ?</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>New Serverless Solutions on Google Cloud for Functions Apps and Containers</title><link>https://glaforge.dev/talks/2019/01/03/new-serverless-solutions-on-google-cloud-for-functions-apps-and-containers/</link><pubDate>Thu, 03 Jan 2019 22:14:19 +0100</pubDate><guid>https://glaforge.dev/talks/2019/01/03/new-serverless-solutions-on-google-cloud-for-functions-apps-and-containers/</guid><description>&lt;p>At &lt;a href="https://voxxeddays.com/microservices/">Voxxed Days Microservices&lt;/a>, in Paris,
I talked about the latest development in serverless solutions on &lt;a href="https://cloud.google.com/products/">Google Cloud Platform&lt;/a>,
to deploy &lt;a href="https://cloud.google.com/functions/">functions&lt;/a>, &lt;a href="https://cloud.google.com/appengine/">apps&lt;/a> 
and even &lt;a href="https://cloud.google.com/blog/products/gcp/bringing-the-best-of-serverless-to-you">containers&lt;/a>.&lt;/p>
&lt;p>I answered an &lt;a href="https://voxxeddays.com/microservices/2018/09/16/guillaume-laforge-on-serverless-solutions-on-google-cloud/">interview&lt;/a> 
on the theme of microservices, and how this maps to the Google cloud products.&lt;/p>
&lt;p>And the video of my presentation was published on YouTube:&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/Ke89_rFx7tI?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video">&lt;/iframe>
&lt;/div>
&lt;p>Here&amp;rsquo;s the abstract of the session:&lt;/p></description><content:encoded>
<![CDATA[<p>At <a href="https://voxxeddays.com/microservices/">Voxxed Days Microservices</a>, in Paris,
I talked about the latest development in serverless solutions on <a href="https://cloud.google.com/products/">Google Cloud Platform</a>,
to deploy <a href="https://cloud.google.com/functions/">functions</a>, <a href="https://cloud.google.com/appengine/">apps</a> 
and even <a href="https://cloud.google.com/blog/products/gcp/bringing-the-best-of-serverless-to-you">containers</a>.</p>
<p>I answered an <a href="https://voxxeddays.com/microservices/2018/09/16/guillaume-laforge-on-serverless-solutions-on-google-cloud/">interview</a> 
on the theme of microservices, and how this maps to the Google cloud products.</p>
<p>And the video of my presentation was published on YouTube:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/Ke89_rFx7tI?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>Here&rsquo;s the abstract of the session:</p>
<blockquote>
<p>Plenty of novelties in the Serverless offering of Google Cloud Platform, whether you&rsquo;re developing functions, apps or containers.</p>
<p>Let&rsquo;s get started with the new modern runtimes for the venerable Google App Engine,
sandboxed thanks to the open source gVisor container sandboxing technology.
Cloud Functions is now GA with Node.js, but also offers new languages like Python to let you implement your functions.
If you need more flexibility, you will also be able to run serverless containers: just dockerize your project and off you go!</p>
<p>But the crux of the show has to be the new open source project, Knative, a collaboration of Google with key vendors like Pivotal,
IBM, Red Hat or SAP, which offers a set of portable building blocks on top of Kubernetes to build serverless platforms.
Additionally, you will be able to try out Knative on Google Kubernetes Engine thanks to a dedicated add-on.</p>
<p>In this session, we&rsquo;ll review all the new serverless-related features of Google Cloud Platform with concrete demos,
so you can get started easily and rapidly.</p></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Deploy a Micronaut application containerized with JIB to Google Kubernetes Engine</title><link>https://glaforge.dev/posts/2018/11/26/deploy-a-micronaut-application-containerized-with-jib-to-google-kubernetes-engine/</link><pubDate>Mon, 26 Nov 2018 22:31:16 +0100</pubDate><guid>https://glaforge.dev/posts/2018/11/26/deploy-a-micronaut-application-containerized-with-jib-to-google-kubernetes-engine/</guid><description>&lt;p>A few weeks ago, I had the chance to be at &lt;a href="https://devoxx.be/">Devoxx Belgium&lt;/a> once again, to meet developers and learn about new things from awesome speakers. Google Cloud Platform had its own booth on the exhibition floor, and the team was running codelabs: 10 laptops were at the disposal of attendees to go through various hands-on tutorials on several GCP products. I took a chance at crafting my own codelab: deploying a Micronaut application, containerized with Jib, to Google Kubernetes Engine.&lt;/p></description><content:encoded>
<![CDATA[<p>A few weeks ago, I had the chance to be at <a href="https://devoxx.be/">Devoxx Belgium</a> once again, to meet developers and learn about new things from awesome speakers. Google Cloud Platform had its own booth on the exhibition floor, and the team was running codelabs: 10 laptops were at the disposal of attendees to go through various hands-on tutorials on several GCP products. I took a chance at crafting my own codelab: deploying a Micronaut application, containerized with Jib, to Google Kubernetes Engine.</p>
<p>For the impatient, follow this link: <a href="https://g.co/codelabs/micronaut">g.co/codelabs/micronaut</a></p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition note">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z"/></svg>
        <span>Note</span>
      </div>
      <div class="admonition-content">
        <p>If you haven&rsquo;t got a GCP account already, know that there&rsquo;s a <a href="https://console.cloud.google.com/freetrial">free trial with $300 of cloud credits</a> to get started.</p>
      </div>
    </div><p>More information on the tools used:</p>
<ul>
<li>
<p><a href="http://micronaut.io/">Micronaut</a> is a modern, JVM-based, full-stack framework for building modular, easily testable microservice and serverless applications. Micronaut aims to deliver great startup time, fast throughput, with a minimal memory footprint. Developers can develop with Micronaut in Java, Groovy or Kotlin.</p>
</li>
<li>
<p>Jib is an open source tool that lets you build Docker and OCI images for your Java applications. It is available as plugins for Maven and Gradle, and as a Java library.</p>
</li>
<li>
<p><a href="https://kubernetes.io/">Kubernetes</a> is an open source project which can run in many different environments, from laptops to high-availability multi-node clusters, from public clouds to on-premise deployments, from virtual machines to bare metal.</p>
</li>
<li>
<p><a href="https://cloud.google.com/kubernetes-engine/">Google Kubernetes Engine</a> is Google Cloud Platform&rsquo;s hosted Kubernetes platform.</p>
</li>
</ul>
<p>In this codelab, you deploy a simple Groovy-based Micronaut microservice to Kubernetes running on Kubernetes Engine.</p>
<p>The goal of this codelab is for you to run your microservice as a replicated service running on Kubernetes. You take code that you have developed on your machine, turn it into a Docker container image built with Jib, and then run and scale that image on Kubernetes Engine.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>An Intro to Google Cloud Platform</title><link>https://glaforge.dev/talks/2018/10/25/an-intro-to-google-cloud-platform/</link><pubDate>Thu, 25 Oct 2018 22:34:39 +0100</pubDate><guid>https://glaforge.dev/talks/2018/10/25/an-intro-to-google-cloud-platform/</guid><description>&lt;p>In a matter of a few years, Google Cloud Platform has evolved from a very small set of products or APIs to a wealth of close to a hundred of products, services and APIs that developers can take advantage of.&lt;/p>
&lt;p>This week, at the event &lt;a href="https://www.meilleurdevdefrance.com/">Le Meilleur Dev de France&lt;/a>, I gave an introduction to the whole platform, focusing on three key axis: compute, storage and machine learning. After an introduction on famous users of GCP, like Snapchat, Spotify or PokemonGo, I also gave a few examples of big French companies as well as French startups who have decided to go to the cloud with Google.&lt;/p></description><content:encoded>
<![CDATA[<p>In a matter of a few years, Google Cloud Platform has evolved from a very small set of products or APIs to a wealth of close to a hundred of products, services and APIs that developers can take advantage of.</p>
<p>This week, at the event <a href="https://www.meilleurdevdefrance.com/">Le Meilleur Dev de France</a>, I gave an introduction to the whole platform, focusing on three key axis: compute, storage and machine learning. After an introduction on famous users of GCP, like Snapchat, Spotify or PokemonGo, I also gave a few examples of big French companies as well as French startups who have decided to go to the cloud with Google.</p>
<p>Later on, over the course of three sections, as I was covering the multiple solutions in each areas (compute / storage / ML), I also tried to give concrete hints as to when to use what, depending on your application needs. Indeed, as many solutions are at your disposal, comes the paradox of choice, as with more options, choice becomes more complicated.</p>
<p>Here are the slides I presented:</p>
<script async class="speakerdeck-embed" data-id="5bc87feeb5914782a2476411c01d353a" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Nice Series on Spring Cloud Integration for Google Cloud Platform</title><link>https://glaforge.dev/posts/2018/09/17/nice-series-on-spring-cloud-integration-for-google-cloud-platform/</link><pubDate>Mon, 17 Sep 2018 22:36:43 +0100</pubDate><guid>https://glaforge.dev/posts/2018/09/17/nice-series-on-spring-cloud-integration-for-google-cloud-platform/</guid><description>&lt;p>My friend and former colleague &lt;a href="https://twitter.com/starbuxman">Josh Long&lt;/a> wrote a nice series of articles showing the Spring Cloud integration for Google Cloud Platform. The series contains 8 articles, covering:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://spring.io/blog/2018/08/20/bootiful-gcp-getting-started-with-spring-cloud-for-google-cloud-platform-1-8">the setup &amp;amp; authentication&lt;/a>, &lt;/li>
&lt;li>how to &lt;a href="https://spring.io/blog/2018/08/23/bootiful-gcp-relational-data-access-with-spring-cloud-gcp-2-8">access relational databases&lt;/a>, &lt;/li>
&lt;li>how to &lt;a href="https://spring.io/blog/2018/08/27/bootiful-gcp-globally-consistent-data-access-with-spanner-3-8">connect to Spanner&lt;/a>, &lt;/li>
&lt;li>how to &lt;a href="https://spring.io/blog/2018/08/30/bootiful-gcp-integration-with-google-cloud-pub-sub-4-8">send/receive messages with Pub/Sub&lt;/a>, &lt;/li>
&lt;li>how to &lt;a href="https://spring.io/blog/2018/09/03/bootiful-gcp-runtime-configuration-with-spring-cloud-gcp-runtime-config-5-8">use the runtime configuration&lt;/a>, &lt;/li>
&lt;li>how to look into &lt;a href="https://spring.io/blog/2018/09/06/bootiful-gcp-supporting-observability-with-spring-cloud-gcp-stackdriver-trace-6-8">Stackdriver tracing&lt;/a>, &lt;/li>
&lt;li>how to &lt;a href="https://spring.io/blog/2018/09/10/bootiful-gcp-use-spring-cloud-gcp-to-connect-to-other-gcp-services-7-8">access other services like the machine learning APIs&lt;/a>, &lt;/li>
&lt;li>and last but not least, how to &lt;a href="https://spring.io/blog/2018/09/13/bootiful-gcp-to-production-8-8">go to production&lt;/a>.&lt;/li>
&lt;/ul>
&lt;p>So if you&amp;rsquo;re using Spring and Spring Boot, this is the way to go for getting started on using Google Cloud Platform. For further reference, you can go read the &lt;a href="https://cloud.spring.io/spring-cloud-gcp/">documentation&lt;/a> that covers this integration.&lt;/p></description><content:encoded>
<![CDATA[<p>My friend and former colleague <a href="https://twitter.com/starbuxman">Josh Long</a> wrote a nice series of articles showing the Spring Cloud integration for Google Cloud Platform. The series contains 8 articles, covering:</p>
<ul>
<li><a href="https://spring.io/blog/2018/08/20/bootiful-gcp-getting-started-with-spring-cloud-for-google-cloud-platform-1-8">the setup &amp; authentication</a>, </li>
<li>how to <a href="https://spring.io/blog/2018/08/23/bootiful-gcp-relational-data-access-with-spring-cloud-gcp-2-8">access relational databases</a>, </li>
<li>how to <a href="https://spring.io/blog/2018/08/27/bootiful-gcp-globally-consistent-data-access-with-spanner-3-8">connect to Spanner</a>, </li>
<li>how to <a href="https://spring.io/blog/2018/08/30/bootiful-gcp-integration-with-google-cloud-pub-sub-4-8">send/receive messages with Pub/Sub</a>, </li>
<li>how to <a href="https://spring.io/blog/2018/09/03/bootiful-gcp-runtime-configuration-with-spring-cloud-gcp-runtime-config-5-8">use the runtime configuration</a>, </li>
<li>how to look into <a href="https://spring.io/blog/2018/09/06/bootiful-gcp-supporting-observability-with-spring-cloud-gcp-stackdriver-trace-6-8">Stackdriver tracing</a>, </li>
<li>how to <a href="https://spring.io/blog/2018/09/10/bootiful-gcp-use-spring-cloud-gcp-to-connect-to-other-gcp-services-7-8">access other services like the machine learning APIs</a>, </li>
<li>and last but not least, how to <a href="https://spring.io/blog/2018/09/13/bootiful-gcp-to-production-8-8">go to production</a>.</li>
</ul>
<p>So if you&rsquo;re using Spring and Spring Boot, this is the way to go for getting started on using Google Cloud Platform. For further reference, you can go read the <a href="https://cloud.spring.io/spring-cloud-gcp/">documentation</a> that covers this integration.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Building and deploying microservices with App Engine and Cloud Functions</title><link>https://glaforge.dev/talks/2018/08/06/building-and-deploying-microservices-with-app-engine-and-cloud-functions/</link><pubDate>Mon, 06 Aug 2018 22:46:46 +0100</pubDate><guid>https://glaforge.dev/talks/2018/08/06/building-and-deploying-microservices-with-app-engine-and-cloud-functions/</guid><description>&lt;p>A coupe weeks ago, I had the chance to talk at &lt;a href="https://cloud.withgoogle.com/next18/sf/">Cloud Next&lt;/a> 2018, in San Francisco, with my colleague and friend &lt;a href="https://twitter.com/alexismp">Alexis&lt;/a>. We talked about building and deploying microservices with Google App Engine and Cloud Functions. I&amp;rsquo;ve been a big fan of App Engine since 2009 when Google released the Java flavor, and have been enjoying doing a bit of Node / JavaScript on Cloud Functions since it came in beta. So I was very happy to be able to talk about those two serverless solutions.&lt;/p></description><content:encoded>
<![CDATA[<p>A coupe weeks ago, I had the chance to talk at <a href="https://cloud.withgoogle.com/next18/sf/">Cloud Next</a> 2018, in San Francisco, with my colleague and friend <a href="https://twitter.com/alexismp">Alexis</a>. We talked about building and deploying microservices with Google App Engine and Cloud Functions. I&rsquo;ve been a big fan of App Engine since 2009 when Google released the Java flavor, and have been enjoying doing a bit of Node / JavaScript on Cloud Functions since it came in beta. So I was very happy to be able to talk about those two serverless solutions.</p>
<p>Without further ado, let&rsquo;s start by sharing the video (and slides) of the talk!</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/oALEthV9z_U?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>Now that I&rsquo;ve shared the video, let me tell you a bit more about this session and the demo we built.</p>
<p><figure>
  <a href="#img-3e0fd5391c74eeb8020949be9c5628d4">
    <img src="/img/microservices-with-ae-cf/gae-gcf-01.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-3e0fd5391c74eeb8020949be9c5628d4">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/microservices-with-ae-cf/gae-gcf-01.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>First, a few words about the buzzword du jour: serverless. What I particularly like about this approach (which I also liked in the good old times of another old buzzword: PaaS) is how it lets me focus on the idea I want to implement, instead of being bothered from the get go with server or cluster provisioning, OS choice, monitoring/logging/alerting, etc. I can directly start coding something, and quickly deploy my app or function in the cloud and see how well it&rsquo;s working (compared to my dreamed up idea). Additionally, besides the ops-less aspect, I also don&rsquo;t have to think about scaling, as it scales pretty much auto-magically for me. Last but not least, I don&rsquo;t have to pay upfront big costs for renting machines or vms, as it&rsquo;s really pay as you go, and not paying for an idle server (after all, my idea might be just a quick idea not geared towards prime-time success!)</p>
<p><figure>
  <a href="#img-ff8bb916efeb567dd6c5ef951260605a">
    <img src="/img/microservices-with-ae-cf/gae-gcf-02.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-ff8bb916efeb567dd6c5ef951260605a">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/microservices-with-ae-cf/gae-gcf-02.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Google Cloud Platform offers various solutions which follow those characteristics, not only for compute with App Engine and Cloud Functions, but also for data storage (like Datastore which I&rsquo;m using in my demo as my database), or the machine learning APIs (like the Vision API that I also integrated in my app). Database-as-a-Service, Platform-as-a-Service, Function-as-a-Service, Software-as-a-Service often fall into that category for me, if you don&rsquo;t pay for infrastructure, when it takes care of scaling, and if it&rsquo;s price proportionally to your usage.</p>
<h2 id="cloud-functions">Cloud Functions</h2>
<p>Cloud Functions (or GCF for short) is a great fit for event driven problems: a new picture is stored on Google Cloud Storage? A function is triggered. I get a message on Pub/Sub? Another function is invoked. It&rsquo;s also possible to invoke a function directly via an HTTP call, without requiring any kind of gateway to expose it.</p>
<p>At Next, the general availability of Cloud Functions was announced, with an SLA of 99.5%, additional regions (2 in the US, 1 in Europe, 1 in APAC), and also new runtimes with Node.js 8 and Python 3.7. Further improvements are the ability to get a function hooked to a VPN in order to connect your functions with your VMs, new scaling controls to limit the number of instances serving your function, a direction connection to Cloud SQL to take advantage of GCP&rsquo;s great network instead of going through the wider public Internet, and the availability of environment variables to customize your deployments for example to tackle different environments like dev vs staging vs prod.</p>
<h2 id="app-engine">App Engine</h2>
<p>As I said, I&rsquo;ve always been a big fan of App Engine, long before I actually joined Google. This blog you&rsquo;re reading has been running on App Engine Java for many years! GAE (for short) is really a great fit for hosting web frontends or backend APIs, which are generally more long-lived that functions.</p>
<p>With Java 8 in GA last year, Node.js 8 in beta, new runtimes are also coming up: Python 3.7 and PHP 7.2. With the recently released new <a href="https://cloudplatform.googleblog.com/2018/05/Increase-performance-while-reducing-costs-with-the-new-App-Engine-scheduler.html">instance scheduler</a>, you have more control on your scaling which allows you to scale faster and have lower costs too. Deployments of new versions should also be faster with better caching and diff&rsquo;ing between versions.</p>
<p>During the hallway session, I had a nice conversation with an attendee who was pretty happy with the fact he&rsquo;d be able to have Python 3.7 for both Cloud Functions and App Engine, which will allow them to have a chance to share some code between projects.</p>
<p>The new runtimes are running on the <a href="https://cloudplatform.googleblog.com/2018/05/Open-sourcing-gVisor-a-sandboxed-container-runtime.html">gVisor sandbox container runtime</a> technology, a lightweight solution to isolate containers securely to run your payloads. A big advantage of gVisor is that App Engine runtimes are not limited anymore with things like the class whitelist which prevented usage of some particular classes.</p>
<p><figure>
  <a href="#img-1d8e05c712cec91f8df68ab78646b039">
    <img src="/img/microservices-with-ae-cf/gae-gcf-03.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-1d8e05c712cec91f8df68ab78646b039">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/microservices-with-ae-cf/gae-gcf-03.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Back to the topic of microservices, App Engine has this concept of services. In your GCP project, your GAE application can run several services at the same time, potentially with different runtimes (for example a Java service and a Go service), and those services can be deployed with different versions.</p>
<p><figure>
  <a href="#img-2b259cedab91581e556f637e3abbd439">
    <img src="/img/microservices-with-ae-cf/gae-gcf-04.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-2b259cedab91581e556f637e3abbd439">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/microservices-with-ae-cf/gae-gcf-04.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Last thing I&rsquo;ll mention here for App Engine, that&rsquo;s the traffic splitting capability. You can easily split traffic (on the command-line or in the web UI) between different versions of a particular service. So for example if you want to do some A/B testing to see if users prefer the new feature or layout of your app, you can say that only 5% of incoming requests will be showing it, whereas the 95% of your users will continue to see the old version. This is also useful for canary deployments or blue / green deployments.</p>
<p><figure>
  <a href="#img-9752c0711ddfda2a1fc28481ea338b62">
    <img src="/img/microservices-with-ae-cf/gae-gcf-05.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-9752c0711ddfda2a1fc28481ea338b62">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/microservices-with-ae-cf/gae-gcf-05.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>For my demo, I developed a simple picture sharing app. My web frontend is a Vue.js + App Engine Java backend using the SparkJava light framework. When a user takes a picture, it&rsquo;s uploaded to Google Cloud Storage, which triggers a Cloud Function which will store picture metadata in Datastore, and calls the Vision API to get the labels of things found in the picture, as well as check if the picture can be safely published (no racy, adult, spoof, violent content in it), and gives the dominant color in the image. Another function is triggered on at regular intervals to compute the most frequent tags (stored in Datastore), so a snapshot of them can be displayed in the dedicate page of the app.</p>
<p><figure>
  <a href="#img-b6b034a4572cb53191b234bfcee5ee9b">
    <img src="/img/microservices-with-ae-cf/gae-gcf-06.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-b6b034a4572cb53191b234bfcee5ee9b">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/microservices-with-ae-cf/gae-gcf-06.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h2 id="scaling-down">Scaling down&hellip;</h2>
<p>Getting to scale down towards our talk conclusion, we also shared a few words about the upcoming serverless containers for Cloud Functions, which we unveiled at the conference and on the GCP <a href="https://cloudplatform.googleblog.com/2018/07/bringing-the-best-of-serverless-to-you.html">blog post</a>. For serverless compute, you can deploy functions and apps, but we&rsquo;re seeing units of compute in the form of containers as well, and sometimes your project might need specific native libraries or particular compute abilities (like GPUs), or you simply want more control over your business logic&rsquo;s environments. So it makes sense to let you serve containers as well, in addition to functions and apps. If you&rsquo;re interested in trying out serverless containers, feel free to request access to the EAP program via <a href="http://g.co/serverlesscontainers">g.co/serverlesscontainers</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>In the top 20 Java influencers for 2018</title><link>https://glaforge.dev/posts/2018/07/30/in-the-top-20-java-influencers-for-2018/</link><pubDate>Mon, 30 Jul 2018 22:52:14 +0100</pubDate><guid>https://glaforge.dev/posts/2018/07/30/in-the-top-20-java-influencers-for-2018/</guid><description>&lt;p>&lt;figure>
&lt;a href="#img-a1ed18e1540a62d1b07c07e6017c02d8">
&lt;img src="https://glaforge.dev/img/misc/JAX_London_2018_Infografik_Influencer_Teaser_600x3700_47308_v1.jpg"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-a1ed18e1540a62d1b07c07e6017c02d8">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/JAX_London_2018_Infografik_Influencer_Teaser_600x3700_47308_v1.jpg"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>Just before heading to &lt;a href="https://cloud.withgoogle.com/next18/sf/">Google Cloud Next&lt;/a>, I was notified I was listed 4th in JAX London&amp;rsquo;s Top 20 Java influencers of 2018 on social media! It&amp;rsquo;s an honor to be listed among famous figures like Josh Bloch, Brian Goetze, Martin Thompson, Arun Gupta, Jessica Kerr, Mario Fusco, Josh Long, Venkat Subramanian, Charles Nutter and many others. You can see the full list of the top influencers &lt;a href="https://jaxlondon.com/blog/java-core-languages/top-20-java-influencers-of-2018/">here&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p><figure>
  <a href="#img-a1ed18e1540a62d1b07c07e6017c02d8">
    <img src="/img/misc/JAX_London_2018_Infografik_Influencer_Teaser_600x3700_47308_v1.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-a1ed18e1540a62d1b07c07e6017c02d8">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/JAX_London_2018_Infografik_Influencer_Teaser_600x3700_47308_v1.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Just before heading to <a href="https://cloud.withgoogle.com/next18/sf/">Google Cloud Next</a>, I was notified I was listed 4th in JAX London&rsquo;s Top 20 Java influencers of 2018 on social media! It&rsquo;s an honor to be listed among famous figures like Josh Bloch, Brian Goetze, Martin Thompson, Arun Gupta, Jessica Kerr, Mario Fusco, Josh Long, Venkat Subramanian, Charles Nutter and many others. You can see the full list of the top influencers <a href="https://jaxlondon.com/blog/java-core-languages/top-20-java-influencers-of-2018/">here</a>.</p>
<p>I&rsquo;ll definitely continue to advocate for Java (and Apache Groovy) developers around the world, and share whatever I learn along the way through articles or conference talks. I&rsquo;m looking forward to meeting you, my fellow Java/Groovy developer friends, at an event near you.</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Congratulations <a href="https://twitter.com/glaforge?ref_src=twsrc%5Etfw">@glaforge</a> for making it to the Top Social Influencers in Java 2018! <a href="https://t.co/rifptQeDcD">https://t.co/rifptQeDcD</a> <a href="https://t.co/kPYGXAaWeQ">pic.twitter.com/kPYGXAaWeQ</a></p>&mdash; JAX London (@jaxlondon) <a href="https://twitter.com/jaxlondon/status/1021684076779065344?ref_src=twsrc%5Etfw">July 24, 2018</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>The Big Green Button Automating Continuous Delivery With Chatbots</title><link>https://glaforge.dev/talks/2018/06/25/the-big-green-button-automating-continuous-delivery-with-chatbots/</link><pubDate>Mon, 25 Jun 2018 09:19:50 +0100</pubDate><guid>https://glaforge.dev/talks/2018/06/25/the-big-green-button-automating-continuous-delivery-with-chatbots/</guid><description>&lt;p>Last month in sunny Napa valley, my awesome colleague &lt;a href="https://www.sethvargo.com/">Seth Vargo&lt;/a> and I had the chance to speak at &lt;a href="https://swampup.jfrog.com/">SwampUp&lt;/a>, the devops focused conference organized by &lt;a href="https://jfrog.com/">JFrog&lt;/a>. Our talk &amp;amp; demo were focused on the topic of &amp;ldquo;ChatOps&amp;rdquo;. But what is ChatOps? Here&amp;rsquo;s what our abstract said:&lt;/p>
&lt;blockquote>
&lt;p>Heard of ChatOps? It&amp;rsquo;s a movement in the DevOps community to take advantage of Chatbots. &lt;/p>
&lt;p>Chatbots centralize the conversation and history of your daily operations including build status, issue management, deployment, and monitoring, so that you access all the information and actions needed at the whim of a chat message in your team communication solution.&lt;/p></description><content:encoded>
<![CDATA[<p>Last month in sunny Napa valley, my awesome colleague <a href="https://www.sethvargo.com/">Seth Vargo</a> and I had the chance to speak at <a href="https://swampup.jfrog.com/">SwampUp</a>, the devops focused conference organized by <a href="https://jfrog.com/">JFrog</a>. Our talk &amp; demo were focused on the topic of &ldquo;ChatOps&rdquo;. But what is ChatOps? Here&rsquo;s what our abstract said:</p>
<blockquote>
<p>Heard of ChatOps? It&rsquo;s a movement in the DevOps community to take advantage of Chatbots. </p>
<p>Chatbots centralize the conversation and history of your daily operations including build status, issue management, deployment, and monitoring, so that you access all the information and actions needed at the whim of a chat message in your team communication solution.</p>
<p>After a quick introduction about the principles and pros&amp;cons of ChatOps, Guillaume Laforge &amp; Seth Vargo will walk you through Dialogflow to design your conversational interface to your CI/CD infrastructure. With the help of demos on the Google Cloud Platform, we&rsquo;ll see together how chatbots can make you more productive in your DevOps activities.</p></blockquote>
<p>The video has been published last week:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/gIE1gQGTc?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Chatbots: switching the second gear</title><link>https://glaforge.dev/talks/2018/06/21/chatbots-switching-the-second-gear/</link><pubDate>Thu, 21 Jun 2018 13:39:51 +0100</pubDate><guid>https://glaforge.dev/talks/2018/06/21/chatbots-switching-the-second-gear/</guid><description>&lt;p>My buddy &lt;a href="https://twitter.com/manekinekko">Wassim&lt;/a> and I were back on stage together to talk about chatbots,
with &lt;a href="https://developers.google.com/actions/">Actions on Google&lt;/a> and &lt;a href="https://twitter.com/manekinekko">Dialogflow&lt;/a>,
at &lt;a href="https://devfest.gdglille.org/">DevFest Lille&lt;/a> and &lt;a href="http://bestofweb.paris/">Best of Web Paris&lt;/a>.
I&amp;rsquo;d like to share with you the slides of the presentation (the video has been recorded and will be available at a later time.)&lt;/p>
&lt;p>You might be interested in those two codelabs to get started on this journey:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://codelabs.developers.google.com/codelabs/actions-1/index.html?index=..%2F..%2Findex#0">Build Actions for the Google Assistant - level 1&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://codelabs.developers.google.com/codelabs/actions-2/index.html?index=..%2F..%2Findex#0">Build Actions for the Google Assistant - level 2&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Here&amp;rsquo;s the presentation given at DevFest Lille:&lt;/p></description><content:encoded>
<![CDATA[<p>My buddy <a href="https://twitter.com/manekinekko">Wassim</a> and I were back on stage together to talk about chatbots,
with <a href="https://developers.google.com/actions/">Actions on Google</a> and <a href="https://twitter.com/manekinekko">Dialogflow</a>,
at <a href="https://devfest.gdglille.org/">DevFest Lille</a> and <a href="http://bestofweb.paris/">Best of Web Paris</a>.
I&rsquo;d like to share with you the slides of the presentation (the video has been recorded and will be available at a later time.)</p>
<p>You might be interested in those two codelabs to get started on this journey:</p>
<ul>
<li><a href="https://codelabs.developers.google.com/codelabs/actions-1/index.html?index=..%2F..%2Findex#0">Build Actions for the Google Assistant - level 1</a></li>
<li><a href="https://codelabs.developers.google.com/codelabs/actions-2/index.html?index=..%2F..%2Findex#0">Build Actions for the Google Assistant - level 2</a></li>
</ul>
<p>Here&rsquo;s the presentation given at DevFest Lille:</p>
<script async class="speakerdeck-embed" data-id="2466bf9bc1544289ba163b1fc4f2589a" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>And at Best of Web Paris:</p>
<script async class="speakerdeck-embed" data-id="afa17255e9ea4db8a4138102cefeeec3" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Putting a Groovy Twist on Cloud Vision</title><link>https://glaforge.dev/posts/2018/06/18/putting-a-groovy-twist-on-cloud-vision/</link><pubDate>Mon, 18 Jun 2018 20:56:23 +0100</pubDate><guid>https://glaforge.dev/posts/2018/06/18/putting-a-groovy-twist-on-cloud-vision/</guid><description>&lt;p>Powerful machine learning APIs are at your fingertips if you&amp;rsquo;re developing with &lt;a href="https://cloud.google.com/">Google Cloud Platform&lt;/a>, as client libraries are available for various programming languages. Today, we&amp;rsquo;re investigating the Cloud Vision API and its Java SDK, using the &lt;a href="http://groovy-lang.org/">Apache Groovy&lt;/a> programming language&amp;mdash;a multi-faceted language for the Java platform that aims to improve developer productivity thanks to a concise, familiar and easy to learn syntax.&lt;/p>
&lt;p>At &lt;a href="https://gr8conf.eu/">GR8Conf Europe&lt;/a>, in Denmark, the conference dedicated to the &lt;a href="http://groovy-lang.org/">Apache Groovy&lt;/a> ecosystem, I spoke about the machine learning APIs provided by &lt;a href="https://cloud.google.com/">Google Cloud Platform&lt;/a>: Vision, Natural Language, Translate, and Speech (both recognition and synthesis). Since it&amp;rsquo;s a groovy conference, we presented samples and demos using a pretty &lt;a href="http://groovy-lang.org/">Groovy&lt;/a> language. I wanted to share the underlying examples with a wider audience, so here&amp;rsquo;s the first of a series of blog posts covering the demos I presented. I&amp;rsquo;ll start with the Google Cloud Vision API, and I will cover the other APIs in future posts.&lt;/p></description><content:encoded>
<![CDATA[<p>Powerful machine learning APIs are at your fingertips if you&rsquo;re developing with <a href="https://cloud.google.com/">Google Cloud Platform</a>, as client libraries are available for various programming languages. Today, we&rsquo;re investigating the Cloud Vision API and its Java SDK, using the <a href="http://groovy-lang.org/">Apache Groovy</a> programming language&mdash;a multi-faceted language for the Java platform that aims to improve developer productivity thanks to a concise, familiar and easy to learn syntax.</p>
<p>At <a href="https://gr8conf.eu/">GR8Conf Europe</a>, in Denmark, the conference dedicated to the <a href="http://groovy-lang.org/">Apache Groovy</a> ecosystem, I spoke about the machine learning APIs provided by <a href="https://cloud.google.com/">Google Cloud Platform</a>: Vision, Natural Language, Translate, and Speech (both recognition and synthesis). Since it&rsquo;s a groovy conference, we presented samples and demos using a pretty <a href="http://groovy-lang.org/">Groovy</a> language. I wanted to share the underlying examples with a wider audience, so here&rsquo;s the first of a series of blog posts covering the demos I presented. I&rsquo;ll start with the Google Cloud Vision API, and I will cover the other APIs in future posts.</p>
<p>Google Cloud Vision API lets you:</p>
<ul>
<li>Get labels of objects and places from your pictures</li>
<li>Detect faces, with precise location of facial features</li>
<li>Determine if the picture is a particular landmark</li>
<li>Check for inappropriate content</li>
<li>Obtain some image attributes information</li>
<li>Find out if the picture is already available elsewhere on the internet</li>
<li>Detect brand logos</li>
<li>Extract text that appears in your images (OCR)</li>
</ul>
<p>You can try out those features online directly at the Cloud Vision API <a href="https://cloud.google.com/vision/">product page</a>.</p>
<p>Here&rsquo;s the output of a visibly egocentric example that I tried on the product page:</p>
<p><figure>
  <a href="#img-a9bc54568f362b9c680af5a228d9382f">
    <img src="/img/groovy-vision/groovy-vision-13lw1.max-800x800.png"
      alt="/img/groovy-vision/groovy-vision-13lw1.max-800x800.png"
       />
  </a>
  <figcaption>/img/groovy-vision/groovy-vision-13lw1.max-800x800.png</figcaption>
</figure>
<div class="lightbox" id="img-a9bc54568f362b9c680af5a228d9382f">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/groovy-vision/groovy-vision-13lw1.max-800x800.png"
    alt="/img/groovy-vision/groovy-vision-13lw1.max-800x800.png"
     />
  <div class="lightbox-caption">/img/groovy-vision/groovy-vision-13lw1.max-800x800.png</div>
</div>
</p>
<p>In this first installment, I focus on two aspects: labels and OCR. But before diving in, I want to illustrate some of the use cases that all of the Vision API features enable, when you want to enhance your apps by integrating the Vision API.</p>
<h2 id="label-detection">Label detection</h2>
<p>What&rsquo;s in the picture? That&rsquo;s what label detection is all about. So for example, if you&rsquo;re showing a picture from your vacation of your family and your dog on the beach, you might get labels like <em>People, Beach, Photograph, Vacation, Fun, Sea, Sky, Sand, Tourism, Summer, Shore, Ocean, Leisure, Coast, Girl, Happiness, Travel, Recreation, Friendship, and Family</em>. Those labels are also accompanied by a percentage confidence score.</p>
<p>Example use cases:</p>
<ul>
<li>If you run a photography website, you might want to let your users search for particular pictures on specific topics. Rather than having someone manually label each and every picture uploaded, you can store labels generated by the API as keywords for your search engine.</li>
<li>If you&rsquo;re building the next friend-finder app for animals, perhaps you want to check that the picture indeed contains a dog. Again, labels will help to tell you if that&rsquo;s the case without manually checking each submitted image.</li>
<li>If you need to find pictures that match a certain theme, you can use the labels to help with automatic categorization.</li>
</ul>
<h2 id="face-detection">Face detection</h2>
<p>The Cloud Vision API can spot faces in your pictures with great precision: it gives you detailed information about the location of the face (with a bounding box), plus the position of each eye, eyebrow, and ear, as well as the nose, lips, mouth, and chin. It also tells you how the face is tilted, and even at what angle!<br />
You can also learn about the sentiment of the person&rsquo;s expression: joy, sorrow, anger, or surprise, for example. In addition, you&rsquo;re told if the face is exposed, blurred, or is sporting  headwear.</p>
<p>Example use cases:</p>
<ul>
<li>You are building a light-hearted app in which users add a mustache or silly hat to uploaded pictures, you now know how to initially position and size the added objects.</li>
<li>If you want to know (or estimate) the number of attendees in a meeting or presentation at a conference, you can use the API to provide a count of the number of faces in the picture.</li>
</ul>
<h2 id="landmark-detection">Landmark detection</h2>
<p>For pictures of famous landmarks, like say, the Eiffel Tower, Buckingham Palace, the Statue of Liberty, or the Golden Gate bridge, the API will tell you which landmark it recognized, and will even give you the GPS location coordinates.</p>
<p>Example use cases:</p>
<ul>
<li>If you want users of your app or site to upload only pictures of a particular location, you could use those coordinates (provided in latitude and longitude) to automatically check that the place photographed is within the right geo-fenced bounding box.</li>
<li>If you want to automatically show pictures on a map in your app, you could take advantage of the GPS coordinates, or automatically enrich tourism websites with pictures from the visitors of specific locales.</li>
</ul>
<h2 id="inappropriate-content-detection">Inappropriate content detection</h2>
<p>Vision API gives you a confidence percentage about different types of potentially inappropriate content in images, including adult content, depictions of violence, medical content (such as surgical procedures or MRIs), and spoofed pictures (with user-added text and marks).</p>
<p>Example use case:</p>
<ul>
<li>If you want to avoid the unwelcome surprise of inappropriate user-generated content showing up in your site or app, you can use this capability to filter images automatically.</li>
</ul>
<h2 id="image-attributes-and-web-annotations">Image attributes and web annotations</h2>
<p>Pictures all have dominant colors, and you can use the Vision API get a sense of which colors are represented in your image and in which proportion. The Cloud Vision API gives you a palette of colors corresponding to your picture.</p>
<p>In addition to color information, it also suggests possible crop hints, so you can crop the picture to different aspect ratios.</p>
<p>You also get information about whether your picture can be found elsewhere on the net, with a list of URLs with matched images as well as full and partial matches.</p>
<p>Beyond the label detection, the API identifies &ldquo;entities&rdquo;, returning to you IDs of those entities from the Google <a href="https://www.google.com/intl/bn/insidesearch/features/search/knowledge.html">Knowledge Graph</a>.</p>
<p>Example use cases:</p>
<ul>
<li>If you want to make your app or website responsive, before loading the full images you may want to show colored placeholders for them. You can get that information with the palette information from the API.</li>
<li>If you&rsquo;d like to automatically crop pictures while keeping their essential aspects, you can use the crop hints.</li>
<li>If you allow photographers to upload their pictures on your website and you want to check that no one is stealing pictures and posting them without proper attribution, you can use the API to check if each picture can be found elsewhere on the web.</li>
<li>For the picture of me in the introduction of this post, Vision API recognized entities like &ldquo;<a href="http://glaforge.appspot.com/">Guillaume Laforge</a>&rdquo; (me!), <a href="http://groovy-lang.org/">Groovy</a> (the programming language I&rsquo;ve been working on since 2003), <a href="https://en.wikipedia.org/wiki/JavaOne">JavaOne</a> (a conference I&rsquo;ve often spoken at), &ldquo;<a href="https://www.manning.com/books/groovy-in-action-second-edition">Groovy in Action</a>&rdquo; (the book I&rsquo;ve co-authored), &ldquo;<a href="https://community.oracle.com/community/java/java-champions">Java Champions</a>&rdquo; (I&rsquo;ve recently been nominated!), and &ldquo;Software Developer&rdquo; (yes, I do code!). Thanks to those entities, you are able to automatically recognize famous people&mdash;more than just me!</li>
</ul>
<h2 id="brand-or-logo-detection">Brand or logo detection</h2>
<p>In addition to text recognition (discussed immediately below), the Vision API tells you if it recognized any logos or brands.</p>
<p>Example use case:</p>
<ul>
<li>If you want your company&rsquo;s brand or products to be displayed on supermarket shelves, you might have people take pictures of those shelves and confirm automatically that your logo is being displayed.</li>
</ul>
<h2 id="ocr-or-text-recognition">OCR or text recognition</h2>
<p>With OCR text detection, you can find text that is displayed in your pictures. Not only does the API gives you the raw text and automatically detects the locale, but you also get all the bounding boxes for the recognized words, as well as a kind of document format, showing the hierarchy of the various blocks of text that appear.</p>
<p>Example use case:</p>
<ul>
<li>When you want to automatically scan expense receipts, enter text rapidly from pictures, or tackle any of the usual use cases for OCR, you can use the API to find and extract any text identified within.</li>
</ul>
<h2 id="time-to-get-groovy">Time to get Groovy!</h2>
<p>Now that I&rsquo;ve provided lots of use cases for where and when you may want to use Vision API, it&rsquo;s time to look at some code, right? So as I said, in this post I&rsquo;ll highlight just two features: label detection and text detection.</p>
<p>Using the <a href="http://groovy-lang.org/">Apache Groovy</a> programming language, I will illustrate two approaches: the first one uses a REST client library like <a href="https://github.com/jwagenleitner/groovy-wslite"><code>groovy-wslite</code></a>, and the second one uses the <a href="https://cloud.google.com/vision/docs/libraries">Java client library</a> provided for the API.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>In order to use Vision API, you&rsquo;ll need to have an account on Google Cloud Platform (GCP). You can benefit from the $300 <a href="https://cloud.google.com/free/">free trial</a> credits, as well as free quotas. For instance, for Vision API, without even consuming your credits, you can make 1000 calls for free every month. You can also take a look at the API&rsquo;s <a href="https://cloud.google.com/vision/pricing">pricing</a> details, once you exceed the quota or your credits.</p>
<p>Briefly, if you&rsquo;re not already using GCP or still don&rsquo;t have an account, please <a href="https://cloud.google.com/getting-started/">register and create one</a>.</p>
<p>Once you&rsquo;ve set up your GCP account, create a cloud project and <a href="https://cloud.google.com/vision/docs/before-you-begin">enable access to Vision API</a> for the project. That&rsquo;s it&mdash;now you&rsquo;re ready to follow the steps detailed below.</p>
<p>Note that after you have enabled the API, you still need to authenticate your client to use the service. There are different possibilities here. In the OCR example, I call the REST API and use an API key passed as a query parameter. In the label detection sample, I use a service account with application default credentials. You can learn more about those approaches in the <a href="https://cloud.google.com/vision/docs/auth">authentication</a> documentation.</p>
<p>Okay, let&rsquo;s get started!</p>
<h2 id="ocr-using-the-vision-rest-endpoint">OCR using the Vision REST endpoint</h2>
<p>During the spring and summer allergy season, many people are interested in which pollens are likely to trigger their allergies. In France (where I live), we have a website that shows a map of the country, and when you hover over your region, it shows you a picture of the active allergens and their levels. However, this is really just a picture with the names of said allergens. So I decided to extract a list of allergens from this image using the Vision API&rsquo;s OCR method:</p>
<p><figure>
  <a href="#img-958c58f79aff641c14669e374f391355">
    <img src="/img/groovy-vision/groovy-vision-2yg2j.max-600x600.png"
      alt="/img/groovy-vision/groovy-vision-2yg2j.max-600x600.png"
       />
  </a>
  <figcaption>/img/groovy-vision/groovy-vision-2yg2j.max-600x600.png</figcaption>
</figure>
<div class="lightbox" id="img-958c58f79aff641c14669e374f391355">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/groovy-vision/groovy-vision-2yg2j.max-600x600.png"
    alt="/img/groovy-vision/groovy-vision-2yg2j.max-600x600.png"
     />
  <div class="lightbox-caption">/img/groovy-vision/groovy-vision-2yg2j.max-600x600.png</div>
</div>
</p>
<p>In Apache Groovy, when calling REST APIs, I often use the <a href="https://github.com/jwagenleitner/groovy-wslite"><code>groovy-wslite</code></a> library. But there are other similar great libraries like <a href="https://http-builder-ng.github.io/http-builder-ng/"><code>HTTPBuilder-NG</code></a>, which offer similar capabilities via a nice syntax too.</p>
<p>To start, let&rsquo;s <a href="http://docs.groovy-lang.org/latest/html/documentation/grape.html">grab</a> the REST client library and instantiate it:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Grab</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;com.github.groovy-wslite:groovy-wslite:1.1.2&#39;</span><span style="color:#666">)</span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">wslite.rest.*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> client <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> RESTClient<span style="color:#666">(</span><span style="color:#4070a0">&#39;https://vision.googleapis.com/v1/&#39;</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>Here&rsquo;s the URL of the picture with the text I&rsquo;m interested in:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> imgUrl <span style="color:#666">=</span> <span style="color:#4070a0">&#34;http://internationalragweedsociety.org/vigilance/d%2094.gif&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> API_KEY <span style="color:#666">=</span> <span style="color:#4070a0">&#39;REPLACE_ME_WITH_REAL_API_KEY&#39;</span>
</span></span></code></pre></div><p>Be sure to change this dummy text for the API key with a proper API key that you can generate from the &ldquo;APIs &amp; services &gt; Credentials&rdquo; section in the cloud console, as explained <a href="https://support.google.com/cloud/answer/6158862?hl=en">here</a>.</p>
<p>Next, I&rsquo;m going to send a post request to the /images:annotate path with the API key as a query parameter. My request is in JSON, and I&rsquo;m using <a href="http://docs.groovy-lang.org/latest/html/documentation/working-with-collections.html">Groovy&rsquo;s nice list and maps syntax</a> to represent that <a href="http://groovy-lang.org/json.html">JSON</a> request, providing the image URL and the feature I want to use (i.e. text detection):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> response <span style="color:#666">=</span> client<span style="color:#666">.</span><span style="color:#4070a0">post</span><span style="color:#666">(</span><span style="color:#002070;font-weight:bold">path:</span> <span style="color:#4070a0">&#39;/images:annotate&#39;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">query:</span> <span style="color:#666">[</span><span style="color:#002070;font-weight:bold">key:</span> API_KEY<span style="color:#666">])</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    type ContentType<span style="color:#666">.</span><span style="color:#4070a0">JSON</span>
</span></span><span style="display:flex;"><span>    json <span style="color:#4070a0">&#34;requests&#34;</span><span style="color:#666">:</span> <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>            <span style="color:#4070a0">&#34;image&#34;</span><span style="color:#666">:</span> <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>                <span style="color:#4070a0">&#34;source&#34;</span><span style="color:#666">:</span> <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#4070a0">&#34;imageUri&#34;</span><span style="color:#666">:</span> imgUrl
</span></span><span style="display:flex;"><span>                <span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>            <span style="color:#666">],</span>
</span></span><span style="display:flex;"><span>            <span style="color:#4070a0">&#34;features&#34;</span><span style="color:#666">:</span> <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>                <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#4070a0">&#34;type&#34;</span><span style="color:#666">:</span> <span style="color:#4070a0">&#34;TEXT_DETECTION&#34;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>            <span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">]</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>This corresponds to the following JSON payload:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>{ <span style="color:#4070a0">&#34;requests&#34;</span><span style="color:#666">:</span> [{
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;image&#34;</span><span style="color:#666">:</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#34;source&#34;</span><span style="color:#666">:</span> { <span style="color:#4070a0">&#34;imageUri&#34;</span><span style="color:#666">:</span> imgUrl }
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;features&#34;</span><span style="color:#666">:</span> [
</span></span><span style="display:flex;"><span>        { <span style="color:#4070a0">&#34;type&#34;</span><span style="color:#666">:</span> <span style="color:#4070a0">&#34;TEXT_DETECTION&#34;</span> }
</span></span><span style="display:flex;"><span>    ]}
</span></span><span style="display:flex;"><span>]}
</span></span></code></pre></div><p>Thanks to Apache Groovy&rsquo;s flexible nature, it&rsquo;s then easy to go through the returned JSON payload to get the list and <code>println</code> all the text annotations and their descriptions (which correspond to the recognized text):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>response<span style="color:#666">.</span><span style="color:#4070a0">json</span><span style="color:#666">.</span><span style="color:#4070a0">responses</span><span style="color:#666">[</span><span style="color:#40a070">0</span><span style="color:#666">].</span><span style="color:#4070a0">textAnnotations</span><span style="color:#666">.</span><span style="color:#4070a0">description</span><span style="color:#666">.</span><span style="color:#4070a0">each</span> <span style="color:#666">{</span> println it <span style="color:#666">}</span>
</span></span></code></pre></div><p>In spite of the low quality of the image and some odd font kerning, the API was able to find allergens like &ldquo;châtaignier&rdquo; (with the correct circumflex accent) and &ldquo;urticacées&rdquo; (acute accent). On the allergen &ldquo;cupressacées,&rdquo; the diacritical mark was missed though, and a space is intertwined, but the font seems to be adding extra space between some letters.</p>
<h2 id="label-detection-with-the-java-client-library">Label detection with the Java client library</h2>
<p>For my second sample, I was inspired by my visit to Copenhagen for <a href="https://gr8conf.eu/">GR8Conf Europe</a>. I decided to see what labels the API would return for a typical picture of the lovely colorful facades of the <a href="https://en.wikipedia.org/wiki/Nyhavn">Nyhavn</a> harbor.</p>
<p><figure>
  <a href="#img-5a962692cea9e760545aa34aae2fcbba">
    <img src="/img/groovy-vision/groovy-vision-39426.max-800x800.jpeg"
      alt="/img/groovy-vision/groovy-vision-39426.max-800x800.jpeg"
       />
  </a>
  <figcaption>/img/groovy-vision/groovy-vision-39426.max-800x800.jpeg</figcaption>
</figure>
<div class="lightbox" id="img-5a962692cea9e760545aa34aae2fcbba">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/groovy-vision/groovy-vision-39426.max-800x800.jpeg"
    alt="/img/groovy-vision/groovy-vision-39426.max-800x800.jpeg"
     />
  <div class="lightbox-caption">/img/groovy-vision/groovy-vision-39426.max-800x800.jpeg</div>
</div>
</p>
<p>Let&rsquo;s <a href="http://docs.groovy-lang.org/latest/html/documentation/grape.html">grab</a> the Java client library for the vision API:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Grab</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;com.google.cloud:google-cloud-vision:1.24.1&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">com.google.cloud.vision.v1.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">com.google.protobuf.*</span>
</span></span></code></pre></div><p>Here&rsquo;s the URL of the picture:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> imgUrl <span style="color:#666">=</span>
</span></span><span style="display:flex;"><span>   <span style="color:#4070a0">&#34;https://upload.wikimedia.org/wikipedia/commons/3/39/Nyhavn_MichaD.jpg&#34;</span>
</span></span><span style="display:flex;"><span>   <span style="color:#666">.</span><span style="color:#4070a0">toURL</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>Now, let&rsquo;s instantiate the ImageAnnotatorClient class. It&rsquo;s a Closeable object, so we can use Groovy&rsquo;s <code>withCloseable{}</code> method:
<code>ImageAnnotatorClient.create().withCloseable { vision -&gt;</code>
We need the bytes of the picture, which we obtain via the <code>.bytes</code> shortcut, and we create a ByteString object from the <a href="https://developers.google.com/protocol-buffers/">protobuf</a> library used by the Vision API:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> imgBytes <span style="color:#666">=</span> ByteString<span style="color:#666">.</span><span style="color:#4070a0">copyFrom</span><span style="color:#666">(</span>imgUrl<span style="color:#666">.</span><span style="color:#4070a0">bytes</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>To create the request with the AnnotateImageRequest builder, we can employ the Groovy <a href="http://docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/Object.html#tap(groovy.lang.Closure)"><code>tap{}</code></a> method to simplify usage of the builder design pattern:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> request <span style="color:#666">=</span> AnnotateImageRequest<span style="color:#666">.</span><span style="color:#4070a0">newBuilder</span><span style="color:#666">().</span><span style="color:#4070a0">tap</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    addFeatures Feature<span style="color:#666">.</span><span style="color:#4070a0">newBuilder</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>                       <span style="color:#666">.</span><span style="color:#4070a0">setType</span><span style="color:#666">(</span>Feature<span style="color:#666">.</span><span style="color:#4070a0">Type</span><span style="color:#666">.</span><span style="color:#4070a0">LABEL_DETECTION</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>                       <span style="color:#666">.</span><span style="color:#4070a0">build</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    setImage Image<span style="color:#666">.</span><span style="color:#4070a0">newBuilder</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>                  <span style="color:#666">.</span><span style="color:#4070a0">setContent</span><span style="color:#666">(</span>imgBytes<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>                  <span style="color:#666">.</span><span style="color:#4070a0">build</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}.</span><span style="color:#4070a0">build</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>In that request, we ask for the label detection feature and pass the image bytes. Next, we call the API with our request and then iterate over the resulting label annotations and their confidence score:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>vision<span style="color:#666">.</span><span style="color:#4070a0">batchAnnotateImages</span><span style="color:#666">([</span>request<span style="color:#666">]).</span><span style="color:#4070a0">responsesList</span><span style="color:#666">.</span><span style="color:#4070a0">each</span> <span style="color:#666">{</span> res <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>res<span style="color:#666">.</span><span style="color:#4070a0">hasError</span><span style="color:#666">())</span> println <span style="color:#4070a0">&#34;Error: ${res.error.message}&#34;</span>
</span></span><span style="display:flex;"><span>    res<span style="color:#666">.</span><span style="color:#4070a0">labelAnnotationsList</span><span style="color:#666">.</span><span style="color:#4070a0">each</span> <span style="color:#666">{</span> annotation <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>        println <span style="color:#4070a0">&#34;${annotation.description.padLeft(20)} (${annotation.score})&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>The labels found (and their confidence) are as follows:</p>
<pre tabindex="0"><code>                waterway (0.97506875)
    water transportation (0.9240114)
                    town (0.9142202)
                   canal (0.8753313)
                    city (0.86910504)
                   water (0.82833123)
                  harbor (0.82821053)
                 channel (0.73568773)
                     sky (0.73083687)
                building (0.6117833)
</code></pre><p>That looks pretty accurate to me!</p>
<h2 id="conclusion">Conclusion</h2>
<p>There are tons of situations where you can benefit from <a href="https://cloud.google.com/vision/">Google Cloud Vision</a>&rsquo;s simple and effective image analysis and your use of the API becomes even groovier when using the <a href="http://groovy-lang.org/">Apache Groovy</a> language! Be sure to try out the API directly from the website with the built-in demo, and get started with our <a href="https://codelabs.developers.google.com/codelabs/cloud-vision-intro/index.html?index=..%2F..%2Findex#0">Cloud Vision codelabs</a>.</p>
<p>If you want to go further, I also encourage you to also have a look at the alpha version of <a href="https://cloud.google.com/automl/">Cloud AutoML Vision</a>: you can extend the Vision API by training it on your own picture dataset. By doing so, you can let Cloud Vision recognize particular objects or elements in your photos with finer-grained labels that are specific to your needs.</p>
<p>Upcoming installments in this series will cover natural language understanding (NLU), text translation, speech recognition, and voice synthesis. So stay tuned!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Automating Chrome Headless mode on App Engine with Node.JS 8</title><link>https://glaforge.dev/posts/2018/06/12/automating-chrome-headless-mode-on-app-engine-with-node-js-8/</link><pubDate>Tue, 12 Jun 2018 13:43:51 +0100</pubDate><guid>https://glaforge.dev/posts/2018/06/12/automating-chrome-headless-mode-on-app-engine-with-node-js-8/</guid><description>&lt;p>On the &lt;a href="http://cloud.google.com/">Google Cloud&lt;/a> front today, the big news is the release of the &lt;a href="https://cloudplatform.googleblog.com/2018/06/Now-you-can-deploy-your-Node-js-app-to-App-Engine-standard-environment.html">new Node.JS 8 runtime for Google App Engine Standard&lt;/a>. It&amp;rsquo;s been a while since a completely new runtime was added to the list of supported platforms (Python, Java, PHP, Go). You could already run anything in custom containers on App Engine Flex, including your own containerized Node app, but now you can have all the nice developer experience on the Standard environment, with fast deployment times, and 0 to 1 to n instance automatic scaling (you can see the difference between those two environments &lt;a href="https://cloud.google.com/appengine/docs/nodejs/">here&lt;/a>).&lt;/p></description><content:encoded>
<![CDATA[<p>On the <a href="http://cloud.google.com/">Google Cloud</a> front today, the big news is the release of the <a href="https://cloudplatform.googleblog.com/2018/06/Now-you-can-deploy-your-Node-js-app-to-App-Engine-standard-environment.html">new Node.JS 8 runtime for Google App Engine Standard</a>. It&rsquo;s been a while since a completely new runtime was added to the list of supported platforms (Python, Java, PHP, Go). You could already run anything in custom containers on App Engine Flex, including your own containerized Node app, but now you can have all the nice developer experience on the Standard environment, with fast deployment times, and 0 to 1 to n instance automatic scaling (you can see the difference between those two environments <a href="https://cloud.google.com/appengine/docs/nodejs/">here</a>).</p>
<p>To play with this new runtime, I decided to follow the steps in this guide about <a href="https://cloud.google.com/appengine/docs/standard/nodejs/using-headless-chrome-with-puppeteer">using Chrome headless with Puppeteer</a>.</p>
<p>As my readers know, I&rsquo;m not really a Node person, and usually dabble more with <a href="http://groovy-lang.org/">Apache Groovy</a> and Java, but this runtime was interesting to me as there&rsquo;s a nice integration with native packages. Let me explain.</p>
<p>The App Engine Node runtime <a href="https://cloud.google.com/appengine/docs/standard/nodejs/reference/system-packages">includes tons of native package out of the box</a>, without requiring you to install anything (except the Node modules that take advantage of those packages, of course.) For instance, if you need to do any audio / video manipulation, there&rsquo;s an ffmpeg package. If you want to deal with Git repositories, there&rsquo;s a git package. Need to manipulate images, there&rsquo;s ImageMagick, etc. And there are usually nice Node wrapper modules around those native components.</p>
<p>Among those system pre-installed packages, there&rsquo;s all the necessary ones to run <a href="https://developers.google.com/web/updates/2017/04/headless-chrome">Headless Chrome</a>, ie. running the Chrome browser but without displaying its window basically.</p>
<p>Furthermore, there&rsquo;s the <a href="https://developers.google.com/web/tools/puppeteer/">Puppeteer</a> Node module, which is a library to control Chrome. With those two, you can completely automate the usage of Chrome on the server-side.</p>
<p>What can you do with that? Well, you can:</p>
<ul>
<li>look at / introspect / manipulate the DOM,</li>
<li>pre-render content for your single page apps,</li>
<li>take screenshots of web pages,</li>
<li>watch a particular page and compute diffs between different versions, etc.</li>
</ul>
<h2 id="lets-get-started">Let&rsquo;s get started!</h2>
<p>Without blindly recopying all the steps explained in the tutorial for <a href="https://cloud.google.com/appengine/docs/standard/nodejs/using-headless-chrome-with-puppeteer">running Chrome headless</a>, I&rsquo;ll simply highlight some of key points. The goal is to let puppeteer take screenshots of webpages.</p>
<p>In your package.json, you need the reference the puppeteer module, and potentially express for handling your web requests:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>  <span style="color:#4070a0">&#34;dependencies&#34;</span><span style="">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;express&#34;</span>: <span style="color:#4070a0">&#34;^4.16.3&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;puppeteer&#34;</span>: <span style="color:#4070a0">&#34;^1.2.0&#34;</span>
</span></span><span style="display:flex;"><span>  }<span style="">,</span>
</span></span></code></pre></div><p>Taking advantage of Node 8&rsquo;s async capabilities, in your <code>app.js</code> file, you can instantiate puppeteer:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> browser <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">await</span> puppeteer.launch({
</span></span><span style="display:flex;"><span>  args<span style="color:#666">:</span> [<span style="color:#4070a0">&#39;--no-sandbox&#39;</span>, <span style="color:#4070a0">&#39;--disable-setuid-sandbox&#39;</span>]
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>Then navigate to the desired page:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">const</span> page <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">await</span> browser.newPage();
</span></span><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">await</span> page.<span style="color:#007020;font-weight:bold">goto</span>(url);
</span></span></code></pre></div><p>Take a screenshot and render it back to the browser:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">const</span> imageBuffer <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">await</span> page.screenshot();
</span></span><span style="display:flex;"><span>  res.set(<span style="color:#4070a0">&#39;Content-Type&#39;</span>, <span style="color:#4070a0">&#39;image/png&#39;</span>);
</span></span><span style="display:flex;"><span>  res.send(imageBuffer);
</span></span></code></pre></div><p>To deploy to the Node runtime, you also need the app.yaml deployment descriptor:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">runtime</span>:<span style="color:#bbb"> </span>nodejs8<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">instance_class</span>:<span style="color:#bbb"> </span>F4_1G<span style="color:#bbb">
</span></span></span></code></pre></div><p>We specify that we want to use the new node runtime, but also that we want a slightly bigger instance to run our Node app, as Chrome is pretty hungry with RAM!</p>
<p>Then deploy your app with the <a href="https://cloud.google.com/sdk/gcloud/">gcloud CLI</a>.</p>
<p>Be sure to check the <a href="https://github.com/GoogleCloudPlatform/nodejs-docs-samples/tree/master/appengine/headless-chrome">whole code on Github</a> for all the details.</p>
<p>One quick remark: although it&rsquo;s not mentioned in the tutorial, when you&rsquo;ll first try to deploy the application, it&rsquo;ll tell you that you need to enable the Container Builder API. The error message will be something like <code>Container Builder has not been used in project xyz before or it is disabled. Enable it by visiting...</code> You just need to follow the indicated URL to enable Container Builder. Container Builder is responsible for containerizing your application to be run on App Engine.</p>
<p>Then I was able to navigate to my app, pass it a URL, and get back the screenshot of the web page at that URL. It&rsquo;s pretty handy if you want to integrate thumbnails of websites you reference in your blog posts, for example, or if you want to see if there are differences between different versions of a web page (for integration testing purposes).</p>
<h2 id="conclusion">Conclusion</h2>
<p>The Java ecosystem has a wealth of libraries for various tasks, but often, there are native libraries which are more fully-featured, and Node generally provides nice wrappers for them. Chrome headless with Puppeteer is one example, but ImageMagick for image manipulation is another great one, where I could not find a good equivalent library in the Java ecosystem. So as they say, use the best tool for the job! In the age of microservices, feel free to use another tech stack that best fit the task at hand. And it&rsquo;s really exciting to see this <a href="https://cloudplatform.googleblog.com/2018/06/Now-you-can-deploy-your-Node-js-app-to-App-Engine-standard-environment.html">new Node 8 runtime for App Engine</a> now being available so that you can take advantage of it in your projects.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Vision recognition with a Groovy twist</title><link>https://glaforge.dev/posts/2018/06/06/vision-recognition-with-a-groovy-twist/</link><pubDate>Wed, 06 Jun 2018 14:05:23 +0100</pubDate><guid>https://glaforge.dev/posts/2018/06/06/vision-recognition-with-a-groovy-twist/</guid><description>&lt;p>Last week at &lt;a href="https://gr8conf.eu/">GR8Conf Europe&lt;/a>, I spoke about the machine learning APIs provided by 
&lt;a href="https://cloud.google.com/">Google Cloud Platform&lt;/a>: Vision, Natural Language, Speech recognition and synthesis, etc.
Since it&amp;rsquo;s GR8Conf, that means showing samples and demos using a pretty &lt;a href="http://groovy-lang.org/">Groovy&lt;/a> language,
and I promised to share my code afterwards. So here&amp;rsquo;s a series of blog posts covering the demos I&amp;rsquo;ve presented.
We&amp;rsquo;ll start with the Vision API.&lt;/p>
&lt;p>The Vision API allows you to:&lt;/p>
&lt;ul>
&lt;li>Get labels of what appears in your pictures,&lt;/li>
&lt;li>Detect faces, with precise location of face features,&lt;/li>
&lt;li>Tell you if the picture is a particular landmark,&lt;/li>
&lt;li>Check for inappropriate content,&lt;/li>
&lt;li>Give you some image attributes information,&lt;/li>
&lt;li>Find if the picture is already available on the net,&lt;/li>
&lt;li>Detects brand logos,&lt;/li>
&lt;li>Or extract text that appears in your images (OCR).&lt;/li>
&lt;/ul>
&lt;p>You can try out those features online directly from the Cloud Vision API product page:&lt;/p></description><content:encoded>
<![CDATA[<p>Last week at <a href="https://gr8conf.eu/">GR8Conf Europe</a>, I spoke about the machine learning APIs provided by 
<a href="https://cloud.google.com/">Google Cloud Platform</a>: Vision, Natural Language, Speech recognition and synthesis, etc.
Since it&rsquo;s GR8Conf, that means showing samples and demos using a pretty <a href="http://groovy-lang.org/">Groovy</a> language,
and I promised to share my code afterwards. So here&rsquo;s a series of blog posts covering the demos I&rsquo;ve presented.
We&rsquo;ll start with the Vision API.</p>
<p>The Vision API allows you to:</p>
<ul>
<li>Get labels of what appears in your pictures,</li>
<li>Detect faces, with precise location of face features,</li>
<li>Tell you if the picture is a particular landmark,</li>
<li>Check for inappropriate content,</li>
<li>Give you some image attributes information,</li>
<li>Find if the picture is already available on the net,</li>
<li>Detects brand logos,</li>
<li>Or extract text that appears in your images (OCR).</li>
</ul>
<p>You can try out those features online directly from the Cloud Vision API product page:</p>
<p><a href="https://cloud.google.com/vision/">https://cloud.google.com/vision/</a></p>
<p>Here&rsquo;s a selfish example output:</p>
<p><figure>
  <a href="#img-ed743079135cdf5928b913d8fbcd8116">
    <img src="/img/vision-groovy/ml-apis-01-try-out&#43;small.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-ed743079135cdf5928b913d8fbcd8116">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/vision-groovy/ml-apis-01-try-out&#43;small.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>In this first installment, I&rsquo;ll focus on two aspects: labels and OCR. But before diving, I wanted to illustrate some of the <strong>use cases</strong> that those features enable, when you want to boost your apps by integrating the Vision API.</p>
<h2 id="label-detection">Label detection</h2>
<p>What&rsquo;s in the picture? That&rsquo;s what label detection is all about. So for example, if you&rsquo;re showing a picture from your vacations with your family and dog on the beach, you might be getting labels like <em>&ldquo;People, Beach, Photograph, Vacation, Fun, Sea, Sky, Sand, Tourism, Summer, Shore, Ocean, Leisure, Coast, Girl, Happiness, Travel, Recreation, Friendship, Family&rdquo;</em>. Those labels are also accompanied by percentage confidence.</p>
<h3 id="example-use-cases">Example use cases:</h3>
<ul>
<li>If your website is a photography website, you might want your users to be able to search for particular pictures on specific topics. Rather than having someone manually label each and every picture uploaded, you can store those labels as keywords for your search engine.</li>
<li>You&rsquo;re building the next Instagram for animals, perhaps you want to check that the picture indeed contains a dog in it. Again labels will help to tell you if that&rsquo;s the case or not.</li>
<li>Those labels can help with automatic categorization as well, so you can more easily find pictures along certain themes.</li>
</ul>
<h2 id="face-detection">Face detection</h2>
<p>Vision API can spot faces in your pictures with great precision, as it gives you detailed information about the location of the face (with a bounding box), as well as the position of each eye, eyebrows, nose, lips / mouth, ear, or chin. It also tells you how your face is tilted, even with which angle!</p>
<p>You can also learn about the sentiment of the person: joy, sorrow, anger, surprise. In addition, you&rsquo;re told if the face is exposed, blurred, or has some headwear.</p>
<h3 id="example-use-cases-1">Example use cases:</h3>
<ul>
<li>Snapchat-style, you want to add a mustache or silly hat to pictures uploaded by your users.</li>
<li>Another handy aspect is that you can also count the number of faces in a picture. For example, if you want to count the number of attendees in a meeting or presentation at a conference, you&rsquo;d have a good estimate of people with the number of faces recognized.</li>
</ul>
<h2 id="landmark-detection">Landmark detection</h2>
<p>If the picture is of a famous landmark, like say, the Eiffel Tower, Buckingham Palace, the Statue of Liberty, the Golden Gate, the API will tell you which landmark it recognized, and will even give you the GPS coordinates of the place.</p>
<h3 id="example-use-cases-2">Example use cases:</h3>
<ul>
<li>Users of your app or site should only upload pictures of a particular location, you could use those coordinates to automatically check that the place photographed is within the right geo-fenced bounding box.</li>
<li>You want to automatically show pictures on a map on your app, you could take advantage of the GPS coordinates, or automatically enrich a tourism websites with pictures from the visitors of that place.</li>
</ul>
<h2 id="inappropriate-content-detection">Inappropriate content detection</h2>
<p>Vision API will give you a percentage confidence about whether the picture contains adult content, is a spoofed picture (with user added text and marks), bears some medical character, displays violence or racy materials.</p>
<h3 id="example-use-case">Example use case:</h3>
<ul>
<li>The main use case here is indeed to be able to filter images automatically, to avoid any bad surprise of user-generated content showing up in your site or app when it shouldn&rsquo;t.</li>
</ul>
<h2 id="image-attributes-and-web-annotations">Image attributes and web annotations</h2>
<p>Pictures all have dominant colors, and you can get a sense of which colors are indeed representing the best your image,
in which proportion. Vision API gives you a palette of colors corresponding to your picture.</p>
<p>In addition to color information, it also suggests possible crop hints, so you could crop the picture to different aspect ratios.</p>
<p>You get information about whether this picture can be seen elsewhere on the net,
with a list of URL with matched images, full matches, or partial matches.</p>
<p>Beyond the label detection, the API identifies &ldquo;entities&rdquo;, returning to you IDs of those entities
from the Google <a href="https://www.google.com/intl/bn/insidesearch/features/search/knowledge.html">Knowledge Graph</a>.</p>
<h3 id="example-use-cases-3">Example use cases:</h3>
<ul>
<li>To make your app or website responsive, before loading the full images, you&rsquo;d like to show colored placeholders for the picture. You can get that information with the palette information returned.</li>
<li>You&rsquo;d like to automatically crop pictures to keep the essential aspects of it.</li>
<li>Photographers upload their pictures on your website, but you want to ensure that no one steals those pictures and put them on the net without proper attribution. You can find out whether this picture can be seen elsewhere on the web.</li>
<li>For the picture of me above, Vision API recognized entities like &ldquo;Guillaume Laforge&rdquo; (me!), Groovy (the programming language I&rsquo;ve been working on since 2003), JavaOne (a conference I&rsquo;ve often spoken at), &ldquo;Groovy in Action&rdquo; (the book I&rsquo;ve co-authored), &ldquo;Java Champions&rdquo; (I&rsquo;ve recently been nominated!), or &ldquo;Software Developer&rdquo; (yes, I still code a bit)</li>
</ul>
<h2 id="brand--logo-detection">Brand / logo detection</h2>
<p>In addition to text recognition, that we&rsquo;ll mention right below, Vision API tells you if it recognized some logos or brands.</p>
<h3 id="example-use-case-1">Example use case:</h3>
<ul>
<li>If you&rsquo;re a brand, have some particular products, and you want your brand or products to be displayed on supermarket shelves, you might have people take pictures of those shelves and confirm automatically if your logo is displayed or not.</li>
</ul>
<h3 id="ocr--text-recognition">OCR / text recognition</h3>
<p>You can find the text that is displayed on your pictures. Not only does it gives you the raw text, but you also get all the bounding boxes for the recognized words, as well as offers a kind of document format, showing the various blocks of texts that appear.</p>
<h3 id="example-use-case-2">Example use case:</h3>
<ul>
<li>You want to automatically scan expense receipts, enter text rapidly from pictures, etc. The usual use cases for OCR!</li>
</ul>
<h2 id="time-to-get-groovy">Time to get Groovy!</h2>
<p>People often wonder where and when they can use the Vision API, I think I&rsquo;ve given enough use cases for now, with detailed explanations. But it&rsquo;s time to show some code, right? So as I said, I&rsquo;ll highlight just two features: label detection and text detection.</p>
<p>Using the Apache Groovy programming language, I wanted to illustrate two approaches: the first one using the a REST client like <a href="https://github.com/jwagenleitner/groovy-wslite">groovy-wslite</a>, and the second one just using the <a href="https://cloud.google.com/vision/docs/libraries">Java SDK</a> provided for the API.</p>
<h3 id="preliminary">Preliminary</h3>
<p>In order to use the Vision API, you&rsquo;ll need to have created an account on Google Cloud Platform (GCP for short).
You can benefit from the $300 of credits of the <a href="https://cloud.google.com/free/">free trial</a>, as well as free quotas.
For instance, for the Vision API, without even using your credits, you can make 1000 calls for free every month.
You can also have a look at the <a href="https://cloud.google.com/vision/pricing">pricing</a> of the API, once you go above the quota or your credits.</p>
<p>Briefly, if you&rsquo;re not already using GCP or have an account on it, please register and create your account on GCP,
then create a cloud project. Once the project is created, 
<a href="https://cloud.google.com/vision/docs/before-you-begin">enable access to Vision API</a>,
and you&rsquo;re ready to follow the steps detailed hereafter.</p>
<p>Although we&rsquo;ve enabled the API, we still need somehow to be authenticated to use that service.
There are different possibilities here.
In the first sample with the OCR example, I&rsquo;m calling the REST API and will be using an API key passed as query parameter,
whereas for my label detection sample, I&rsquo;m using a service account with applicaction default credentials.
You can learn more about those approaches in the documentation on <a href="https://cloud.google.com/vision/docs/auth">authentication</a>.</p>
<p>Okay, let&rsquo;s get started!</p>
<h3 id="ocr-calling-the-vision-rest-endpoint">OCR calling the Vision REST endpoint</h3>
<p>With spring and summer, allergetic people might be interested in which pollens are going to cause their allergies to bother them. Where I live, in France, there&rsquo;s a website showing a map of the country, and you can hover the region where you are, and see a picture of the allergens and their levels. However, this is really just a picture with the names of said allergens. So I decided to get their list with the Vision API.</p>
<p><figure>
  <a href="#img-c1f00fb33bc4561b4fe2b2e029673a22">
    <img src="/img/vision-groovy/ml-apis-03-allergens-labels&#43;small.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-c1f00fb33bc4561b4fe2b2e029673a22">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/vision-groovy/ml-apis-03-allergens-labels&#43;small.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>In Apache Groovy, when calling REST APIs, I often use the <a href="https://github.com/jwagenleitner/groovy-wslite">groovy-wslite</a> library. But there are other similar great libraries like <a href="https://http-builder-ng.github.io/http-builder-ng/">HTTPBuilder-NG</a>, which offer similar capabilities with nice syntax too.</p>
<p>Let&rsquo;s <a href="http://docs.groovy-lang.org/latest/html/documentation/grape.html">grab</a> the REST client library and instantiate it:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Grab</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;com.github.groovy-wslite:groovy-wslite:1.1.2&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">wslite.rest.*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> client <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> RESTClient<span style="color:#666">(</span><span style="color:#4070a0">&#39;https://vision.googleapis.com/v1/&#39;</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>Here&rsquo;s the URL of the picture whose text I&rsquo;m interested in:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> imgUrl <span style="color:#666">=</span> <span style="color:#4070a0">&#34;http://internationalragweedsociety.org/vigilance/d%2094.gif&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> API_KEY <span style="color:#666">=</span> <span style="color:#4070a0">&#39;REPLACE_ME_WITH_REAL_API_KEY&#39;</span>
</span></span></code></pre></div><p>Then I&rsquo;m going to send a post request to the <code>/images:annotate</code> path with the API key as query parameter.
My request is in JSON, and I&rsquo;m using Groovy&rsquo;s nice list &amp; maps syntax to represent that JSON request,
providing the image URL and the feature I&rsquo;m interested in (ie. text detection):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> response <span style="color:#666">=</span> client<span style="color:#666">.</span><span style="color:#4070a0">post</span><span style="color:#666">(</span><span style="color:#002070;font-weight:bold">path:</span> <span style="color:#4070a0">&#39;/images:annotate&#39;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">query:</span> <span style="color:#666">[</span><span style="color:#002070;font-weight:bold">key:</span> API_KEY<span style="color:#666">])</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    type ContentType<span style="color:#666">.</span><span style="color:#4070a0">JSON</span>
</span></span><span style="display:flex;"><span>    json <span style="color:#4070a0">&#34;requests&#34;</span><span style="color:#666">:</span> <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>            <span style="color:#4070a0">&#34;image&#34;</span><span style="color:#666">:</span> <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>                <span style="color:#4070a0">&#34;source&#34;</span><span style="color:#666">:</span> <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#4070a0">&#34;imageUri&#34;</span><span style="color:#666">:</span> imgUrl
</span></span><span style="display:flex;"><span>                <span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>            <span style="color:#666">],</span>
</span></span><span style="display:flex;"><span>            <span style="color:#4070a0">&#34;features&#34;</span><span style="color:#666">:</span> <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>                <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#4070a0">&#34;type&#34;</span><span style="color:#666">:</span> <span style="color:#4070a0">&#34;TEXT_DETECTION&#34;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>            <span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">]</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>This corresponds to the following JSON payload:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;requests&#34;</span>: [
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&#34;image&#34;</span>: {
</span></span><span style="display:flex;"><span>                <span style="color:#062873;font-weight:bold">&#34;source&#34;</span>: {
</span></span><span style="display:flex;"><span>                    <span style="color:#062873;font-weight:bold">&#34;imageUri&#34;</span>: <span style="">imgUrl</span>
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&#34;features&#34;</span>: [
</span></span><span style="display:flex;"><span>                {
</span></span><span style="display:flex;"><span>                    <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#4070a0">&#34;TEXT_DETECTION&#34;</span>
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>            ]
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Thanks to Groovy&rsquo;s flexible nature, it&rsquo;s then easy to go through the returned JSON payload to get the list and println all the text annotations and their description (which corresponds to the recognized text):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>println response<span style="color:#666">.</span><span style="color:#4070a0">json</span><span style="color:#666">.</span><span style="color:#4070a0">responses</span><span style="color:#666">[</span><span style="color:#40a070">0</span><span style="color:#666">].</span><span style="color:#4070a0">textAnnotations</span><span style="color:#666">.</span><span style="color:#4070a0">description</span>
</span></span></code></pre></div><h3 id="label-detection-with-the-java-client-library">Label detection with the Java client library</h3>
<p>For my second sample, as I visited Copenhagen for <a href="https://gr8conf.eu/">GR8Conf Europe</a>,
I decided to see what labels the API would return for a typical picture of the lovely colorful facades of the 
<a href="https://en.wikipedia.org/wiki/Nyhavn">Nyhavn</a> harbor.</p>
<p><figure>
  <a href="#img-5488655e03d848109ff999212eb51f43">
    <img src="/img/vision-groovy/ml-apis-02-nyhavn&#43;small.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-5488655e03d848109ff999212eb51f43">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/vision-groovy/ml-apis-02-nyhavn&#43;small.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Let&rsquo;s <a href="http://docs.groovy-lang.org/latest/html/documentation/grape.html">grab</a> the Java client library for the vision API:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Grab</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;com.google.cloud:google-cloud-vision:1.24.1&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">com.google.cloud.vision.v1.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">com.google.protobuf.*</span>
</span></span></code></pre></div><p>Here&rsquo;s the URL of the picture:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> imgUrl <span style="color:#666">=</span> <span style="color:#4070a0">&#34;https://upload.wikimedia.org/wikipedia/commons/3/39/Nyhavn_MichaD.jpg&#34;</span> <span style="color:#666">.</span><span style="color:#4070a0">toURL</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>Let&rsquo;s instantiate the ImageAnnotatorClient class. It&rsquo;s a closeable object, so we can use Groovy&rsquo;s withCloseable{} method:</p>
<pre tabindex="0"><code>ImageAnnotatorClient.create().withCloseable { vision -&gt;
</code></pre><p>We need the bytes of the picture, that we obtain with the <code>.bytes</code> shortcut, and we create a <code>ByteString</code> object from the <a href="https://developers.google.com/protocol-buffers/">protobuf</a> library used by the Vision API:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> imgBytes <span style="color:#666">=</span> ByteString<span style="color:#666">.</span><span style="color:#4070a0">copyFrom</span><span style="color:#666">(</span>imgUrl<span style="color:#666">.</span><span style="color:#4070a0">bytes</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>Now it&rsquo;s time to create our request, with the @AnnotateImageRequest` builder,
using Groovy&rsquo;s <a href="http://docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/Object.html#tap(groovy.lang.Closure)">tap{}</a> 
method to make the use of the builder pattern easier:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> request <span style="color:#666">=</span> AnnotateImageRequest<span style="color:#666">.</span><span style="color:#4070a0">newBuilder</span><span style="color:#666">().</span><span style="color:#4070a0">tap</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    addFeatures Feature<span style="color:#666">.</span><span style="color:#4070a0">newBuilder</span><span style="color:#666">().</span><span style="color:#4070a0">setType</span><span style="color:#666">(</span>Feature<span style="color:#666">.</span><span style="color:#4070a0">Type</span><span style="color:#666">.</span><span style="color:#4070a0">LABEL_DETECTION</span><span style="color:#666">).</span><span style="color:#4070a0">build</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    setImage    Image<span style="color:#666">.</span><span style="color:#4070a0">newBuilder</span><span style="color:#666">().</span><span style="color:#4070a0">setContent</span><span style="color:#666">(</span>imgBytes<span style="color:#666">).</span><span style="color:#4070a0">build</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}.</span><span style="color:#4070a0">build</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>In that request, we ask for the label detection feature, and pass the image bytes.
Then it&rsquo;s time to call the API with our request, and iterate over the resulting label annotations and their confidence score:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>vision<span style="color:#666">.</span><span style="color:#4070a0">batchAnnotateImages</span><span style="color:#666">([</span>request<span style="color:#666">])</span>
</span></span><span style="display:flex;"><span>      <span style="color:#666">.</span><span style="color:#4070a0">responsesList</span><span style="color:#666">.</span><span style="color:#4070a0">each</span> <span style="color:#666">{</span> res <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>res<span style="color:#666">.</span><span style="color:#4070a0">hasError</span><span style="color:#666">())</span>
</span></span><span style="display:flex;"><span>        println <span style="color:#4070a0">&#34;Error: ${res.error.message}&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    res<span style="color:#666">.</span><span style="color:#4070a0">labelAnnotationsList</span><span style="color:#666">.</span><span style="color:#4070a0">each</span> <span style="color:#666">{</span> annotation <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>        println <span style="color:#4070a0">&#34;${annotation.description.padLeft(20)} (${annotation.score})&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>  <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>The labels found (and their confidence) are the following:</p>
<pre tabindex="0"><code>            waterway (0.97506875)
water transportation (0.9240114)
                town (0.9142202)
               canal (0.8753313)
                city (0.86910504)
               water (0.82833123)
              harbor (0.82821053)
             channel (0.73568773)
                 sky (0.73083687)
            building (0.6117833)
</code></pre><p>Pretty good job!</p>
<h2 id="conclusion">Conclusion</h2>
<p>First of all, there are tons of situations where you can benefit from image recognition thanks to <a href="https://cloud.google.com/vision/">Google Cloud Vision</a>. Secondly, it can get even groovier when using the <a href="http://groovy-lang.org/">Apache Groovy</a> language!</p>
<p>In the upcoming installment, we&rsquo;ll speak about natural language understanding, text translation, speech recognition and voice synthesis. So stay tuned!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Machine learning APIs and AI panel discussion at QCon</title><link>https://glaforge.dev/talks/2018/06/04/machine-learning-apis-and-ai-panel-discussion-at-qcon/</link><pubDate>Mon, 04 Jun 2018 16:37:09 +0100</pubDate><guid>https://glaforge.dev/talks/2018/06/04/machine-learning-apis-and-ai-panel-discussion-at-qcon/</guid><description>&lt;p>Last March, I had the chance to attend and speak at &lt;a href="https://qconlondon.com/london2018/schedule/london2018/tabular.html">QCon London&lt;/a>. I spoke at the event for its first edition, many moons prior, so it was fun coming back and seeing how the conference evolved. &lt;/p>
&lt;p>This year, &lt;a href="https://twitter.com/erichoresnyi">Eric Horesnyi&lt;/a> of &lt;a href="https://streamdata.io/">Streamdata&lt;/a> 
was leading the Artificial Intelligence track, and invited me to speak about Machine Learning.&lt;/p>
&lt;p>First, I gave an overview of the Machine Learning offering, from the off-the-shelf ready-made APIs like 
&lt;a href="https://cloud.google.com/vision/">Vision&lt;/a>, &lt;a href="https://cloud.google.com/speech-to-text/">Speech&lt;/a>, 
&lt;a href="https://cloud.google.com/natural-language/">Natural Language&lt;/a>, &lt;a href="https://cloud.google.com/video-intelligence/">Video Intelligence&lt;/a>.
I also mentioned &lt;a href="https://cloud.google.com/automl/">AutoML&lt;/a>,
to further train existing models like the Vision model in order to recognize your own specific details in pictures.
For chatbots, I also covered &lt;a href="https://dialogflow.com/">Dialogflow&lt;/a>.
And I said a few words about Tensorflow and &lt;a href="https://cloud.google.com/ml-engine/">Cloud Machine Learning Engine&lt;/a> 
for training &amp;amp; running your &lt;a href="https://www.tensorflow.org/">Tensorflow&lt;/a> models in the cloud.
You can watch the video by clicking on the picture below:&lt;/p></description><content:encoded>
<![CDATA[<p>Last March, I had the chance to attend and speak at <a href="https://qconlondon.com/london2018/schedule/london2018/tabular.html">QCon London</a>. I spoke at the event for its first edition, many moons prior, so it was fun coming back and seeing how the conference evolved. </p>
<p>This year, <a href="https://twitter.com/erichoresnyi">Eric Horesnyi</a> of <a href="https://streamdata.io/">Streamdata</a> 
was leading the Artificial Intelligence track, and invited me to speak about Machine Learning.</p>
<p>First, I gave an overview of the Machine Learning offering, from the off-the-shelf ready-made APIs like 
<a href="https://cloud.google.com/vision/">Vision</a>, <a href="https://cloud.google.com/speech-to-text/">Speech</a>, 
<a href="https://cloud.google.com/natural-language/">Natural Language</a>, <a href="https://cloud.google.com/video-intelligence/">Video Intelligence</a>.
I also mentioned <a href="https://cloud.google.com/automl/">AutoML</a>,
to further train existing models like the Vision model in order to recognize your own specific details in pictures.
For chatbots, I also covered <a href="https://dialogflow.com/">Dialogflow</a>.
And I said a few words about Tensorflow and <a href="https://cloud.google.com/ml-engine/">Cloud Machine Learning Engine</a> 
for training &amp; running your <a href="https://www.tensorflow.org/">Tensorflow</a> models in the cloud.
You can watch the video by clicking on the picture below:</p>
<p><a href="https://www.infoq.com/presentations/google-ml-services"><figure>
  <a href="#img-3da3042e2c59346eb27db051dbd29c98">
    <img src="/img/qcon-2018/qcon-ml-talk.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-3da3042e2c59346eb27db051dbd29c98">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/qcon-2018/qcon-ml-talk.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</a></p>
<p>Eric hosted a panel discussion with all the speakers in the AI track, where we discussed many interesting topics,
to demystify AI and answer questions from the audience. Click on the picture below to watch the panel discussion:</p>
<p><a href="https://www.infoq.com/presentations/ai-panel"><figure>
  <a href="#img-68ae417a84772fc7e20bceb20fd674fc">
    <img src="/img/qcon-2018/qcon-ai-panel.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-68ae417a84772fc7e20bceb20fd674fc">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/qcon-2018/qcon-ai-panel.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</a></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Getting started with Groovy technologies on Google Cloud Platform</title><link>https://glaforge.dev/talks/2018/06/01/getting-started-with-groovy-technologies-on-google-cloud-platform/</link><pubDate>Fri, 01 Jun 2018 17:25:16 +0100</pubDate><guid>https://glaforge.dev/talks/2018/06/01/getting-started-with-groovy-technologies-on-google-cloud-platform/</guid><description>&lt;p>Back to &lt;a href="https://gr8conf.eu/">GR8Conf Europe&lt;/a> in Denmark, for the yearly &lt;a href="http://www.groovy-lang.org/">Groovy&lt;/a> community reunion! I had the chance to present two talks.&lt;/p>
&lt;p>The first one on Google&amp;rsquo;s &lt;a href="https://cloud.google.com/products/machine-learning/">Machine Learning APIs&lt;/a>, with samples in Groovy using vision recognition, speech recognition &amp;amp; generation, natural language analysis. I&amp;rsquo;ll come back on ML in Groovy in forthcoming articles.&lt;/p>
&lt;p>And the second talk was an overview of Google Cloud Platform, focusing on the compute and storage options, with demos using Groovy frameworks (&lt;a href="https://ratpack.io/">Ratpack&lt;/a>, &lt;a href="http://gaelyk.appspot.com/">Gaelyk&lt;/a>, and the newly released &lt;a href="http://micronaut.io/">Micronaut&lt;/a>) and how to deploy apps on Compute Engine, Kubernetes Engine, App Engine. I&amp;rsquo;ll also come back in further articles on those demos, but in the meantime, I wanted to share my slide deck with you all! Without further ado, here&amp;rsquo;s what I presented:&lt;/p></description><content:encoded>
<![CDATA[<p>Back to <a href="https://gr8conf.eu/">GR8Conf Europe</a> in Denmark, for the yearly <a href="http://www.groovy-lang.org/">Groovy</a> community reunion! I had the chance to present two talks.</p>
<p>The first one on Google&rsquo;s <a href="https://cloud.google.com/products/machine-learning/">Machine Learning APIs</a>, with samples in Groovy using vision recognition, speech recognition &amp; generation, natural language analysis. I&rsquo;ll come back on ML in Groovy in forthcoming articles.</p>
<p>And the second talk was an overview of Google Cloud Platform, focusing on the compute and storage options, with demos using Groovy frameworks (<a href="https://ratpack.io/">Ratpack</a>, <a href="http://gaelyk.appspot.com/">Gaelyk</a>, and the newly released <a href="http://micronaut.io/">Micronaut</a>) and how to deploy apps on Compute Engine, Kubernetes Engine, App Engine. I&rsquo;ll also come back in further articles on those demos, but in the meantime, I wanted to share my slide deck with you all! Without further ado, here&rsquo;s what I presented:</p>
<script async class="speakerdeck-embed" data-id="a9b1167d2ffb418082553a879f51c869" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<blockquote>
<p>Time to release your marvelous project or website, powered with GR8 tech, into the wild?
Let&rsquo;s see together what Google Cloud Platform has to provide for the Apache Groovy ecosystem developer.
If you have a Grails or Ratpack app, what are the best compute options for deploying it?
What other services are useful to your app: databases with Cloud SQL, messaging
with Cloud Pub/Sub, monitoring, and more, are at your disposal.
We&rsquo;ll explore together what options you have to deploy and scale your next great idea in the Google cloud.</p></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Ten years of App Engine with a Groovy twist</title><link>https://glaforge.dev/posts/2018/04/16/ten-years-of-app-engine-with-a-groovy-twist/</link><pubDate>Mon, 16 Apr 2018 17:29:24 +0100</pubDate><guid>https://glaforge.dev/posts/2018/04/16/ten-years-of-app-engine-with-a-groovy-twist/</guid><description>&lt;p>The venerable Google &lt;a href="https://cloud.google.com/appengine/">App Engine&lt;/a> platform celebrated its &lt;a href="https://cloudplatform.googleblog.com/2018/04/reflecting-on-our-ten-year-App-Engine-journey.html">10th anniversary&lt;/a>!&lt;/p>
&lt;p>Back in 2008, it started with Python, as its first runtime, but I got way more interested in App Engine when the Java runtime would launch the following year. It&amp;rsquo;s a bit of a special story for me, as I&amp;rsquo;ve always been a fan of App Engine, since the beginning.&lt;/p>
&lt;p>Over the years, I&amp;rsquo;ve built several apps running on App Engine. For instance, this blog you&amp;rsquo;re reading now is running on App Engine, as well as my personal picture / video sharing app, some Github post-commit webhook for the &lt;a href="http://www.groovy-lang.org/">Apache Groovy&lt;/a> project, or the &lt;a href="http://groovyconsole.appspot.com/">Groovy Web Console&lt;/a> to share / edit / run Groovy scripts in the cloud.&lt;/p></description><content:encoded>
<![CDATA[<p>The venerable Google <a href="https://cloud.google.com/appengine/">App Engine</a> platform celebrated its <a href="https://cloudplatform.googleblog.com/2018/04/reflecting-on-our-ten-year-App-Engine-journey.html">10th anniversary</a>!</p>
<p>Back in 2008, it started with Python, as its first runtime, but I got way more interested in App Engine when the Java runtime would launch the following year. It&rsquo;s a bit of a special story for me, as I&rsquo;ve always been a fan of App Engine, since the beginning.</p>
<p>Over the years, I&rsquo;ve built several apps running on App Engine. For instance, this blog you&rsquo;re reading now is running on App Engine, as well as my personal picture / video sharing app, some Github post-commit webhook for the <a href="http://www.groovy-lang.org/">Apache Groovy</a> project, or the <a href="http://groovyconsole.appspot.com/">Groovy Web Console</a> to share / edit / run Groovy scripts in the cloud.</p>
<p>App Engine is my go-to platform for deploying and running my ideas in the cloud!</p>
<p>I like to focus on the idea I want to implement, rather than thinking upfront about infrastructure, provisioning, ops, etc. App Engine was the pioneer in PaaS (Platform-as-a-Service) and the new trendy <a href="https://cloud.google.com/serverless/">Serverless</a> approach.</p>
<p>Although I&rsquo;ve ranted back in the day about the pricing changes (<a href="https://glaforge.dev/posts/2011/09/01/google-app-engine-s-new-pricing-model/">once</a> and <a href="https://glaforge.dev/posts/2011/11/25/coming-back-to-the-new-google-app-engine-pricing-policy/">twice</a>), it lead me to optimize my own apps and code. But ultimately, most of my apps run within the <a href="https://cloud.google.com/free/">free tier</a> of App Engine. The &ldquo;pay-as-you-go&rdquo; approach is appealing: for my apps, it&rsquo;s been pretty much free for my use, except on those few occasions where I had big peaks of traffic and users, and then, i only had to spend a few dollars to cope with the load, but I didn&rsquo;t even have to think about dealing with infrastructure, as App Engine was transparently scaling my apps itself, without any intervention on my part.</p>
<p><figure>
  <a href="#img-5b1f01e663ce9247dd0108dab075ba1d">
    <img src="/img/misc/google-app-engine-groovy.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-5b1f01e663ce9247dd0108dab075ba1d">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/google-app-engine-groovy.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>But let&rsquo;s step back a little and let me tell you more about my story with App Engine. In 2009, thanks to my friend Dick Wall, I was contacted by Google, signed an NDA, and worked with the engineering team who was responsible for the upcoming Java runtime. As the engineering team was working on its launch, they wanted to ensure that alternative languages like <a href="http://www.groovy-lang.org/">Apache Groovy</a> would run well on the platform. So we worked hand in hand, patching Groovy to be more compliant with App Engine&rsquo;s sandboxing mechanism (which is now lifted, as past limitations are now gone in the newer runtimes.)</p>
<p>Thanks to this work on the Groovy and App Engine integration, I got the chance to present at Google I/O 2009 about running Groovy and Grails on App Engine!</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/NEnniZTdOYk?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>And as I worked on the integration, I quickly found nice handy shortcuts thanks to the flexible nature of Groovy, and I arranged those shortcuts into a reusable library: the <a href="http://gaelyk.appspot.com/">Gaelyk</a> framework.</p>
<p>Max Ross, Toby Reyelts, Don Scwhartz, Dick Wall, Patrick Chanezon, Christian Schalk, and later on, Ludovic Champenois, Éamonn McManus, Roberto Chinnici, and many others, I&rsquo;d like to say thank you, congratulations, and happy anniversary for this lovely platform!</p>
<p>It&rsquo;s an honor for me today to <a href="https://glaforge.dev/posts/2016/06/02/joining-google-as-a-developer-advocate-for-the-google-cloud-platform/">work for Google Cloud Platform</a> (almost 2 years already!), and to use the awesome serverless products available, and I&rsquo;m looking forward to covering the <a href="https://cloud.google.com/serverless/">serverless</a> area even more!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>What can we learn from millions of (groovy) source files in Github</title><link>https://glaforge.dev/talks/2018/03/23/what-do-we-learn-from-millions-of-source-files-in-github/</link><pubDate>Fri, 23 Mar 2018 15:32:04 +0100</pubDate><guid>https://glaforge.dev/talks/2018/03/23/what-do-we-learn-from-millions-of-source-files-in-github/</guid><description>&lt;p>What can you learn from millions of (Groovy) source files stored on Github?
In this presentation, I analized source files in the Github archives stored on BigQuery,
and in particular Groovy source file, but also Gradle build files, or Grails controllers and services.&lt;/p>
&lt;script async class="speakerdeck-embed" data-id="c9876f9890d84d378b5b18c9b57ad7aa" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js">&lt;/script>
&lt;p>What kind of questions can we answer&lt;/p>
&lt;ul>
&lt;li>How many Groovy files are there on Github?&lt;/li>
&lt;li>What are the most popular Groovy file names?&lt;/li>
&lt;li>How many lines of Groovy source code are there?&lt;/li>
&lt;li>What&amp;rsquo;s the distribution of size of source files?&lt;/li>
&lt;li>What are the most frequent imported packages?&lt;/li>
&lt;li>What are the most popular Groovy APIs used?&lt;/li>
&lt;li>What are the most used AST transformations?&lt;/li>
&lt;li>Do people use import aliases much?&lt;/li>
&lt;li>Did developers adopt traits?&lt;/li>
&lt;/ul>
&lt;p>For &lt;a href="https://gradle.org/">Gradle&lt;/a>, here are the questions that I answered:&lt;/p></description><content:encoded>
<![CDATA[<p>What can you learn from millions of (Groovy) source files stored on Github?
In this presentation, I analized source files in the Github archives stored on BigQuery,
and in particular Groovy source file, but also Gradle build files, or Grails controllers and services.</p>
<script async class="speakerdeck-embed" data-id="c9876f9890d84d378b5b18c9b57ad7aa" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>What kind of questions can we answer</p>
<ul>
<li>How many Groovy files are there on Github?</li>
<li>What are the most popular Groovy file names?</li>
<li>How many lines of Groovy source code are there?</li>
<li>What&rsquo;s the distribution of size of source files?</li>
<li>What are the most frequent imported packages?</li>
<li>What are the most popular Groovy APIs used?</li>
<li>What are the most used AST transformations?</li>
<li>Do people use import aliases much?</li>
<li>Did developers adopt traits?</li>
</ul>
<p>For <a href="https://gradle.org/">Gradle</a>, here are the questions that I answered:</p>
<ul>
<li>How many Gradle build files are there?</li>
<li>How many Maven build files are there?</li>
<li>Which versions of Gradle are being used?</li>
<li>How many of those Gradle files are settings files?</li>
<li>What are the most frequent build file names?</li>
<li>What are the most frequent Gradle plugins?</li>
<li>What are the most frequent &ldquo;compile&rdquo; and &ldquo;test&rdquo; dependencies?</li>
</ul>
<p>And for <a href="https://grails.org/">Grails</a>, here&rsquo;s what I covered:</p>
<ul>
<li>What are the most used SQL database used?</li>
<li>What are the most frequent controller names?</li>
<li>What are the repositories with the biggest number of controllers?</li>
<li>What is the distribution of number of controllers?</li>
</ul>
<p>You can see a version of this talk in French in the following YouTube video, recorded at the BreizhCamp conference:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/wk2CRBRrki8?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>And in English at Devoxx US:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/Aw4sgZ8kIeg?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Keynote: IT holy wars</title><link>https://glaforge.dev/talks/2018/03/12/keynote-it-holy-wars/</link><pubDate>Mon, 12 Mar 2018 15:36:40 +0100</pubDate><guid>https://glaforge.dev/talks/2018/03/12/keynote-it-holy-wars/</guid><description>&lt;p>A few months ago, I had some fun doing the keynote of Voxxed Days Singapore and JUG Summer Camp, and I realized I didn&amp;rsquo;t get a chance to share my deck. Furthermore, the videos of both events are actually available online: one in English, one in French!&lt;/p>
&lt;p>I spoke about the so-called IT Holy Wars, you know, like Vi vs Emacs, Tabs &amp;amp; Spaces, or other funny things of that kind. How developers circle from client-side to server-side, how we are polarized across strong positions on certain frameworks or practices, on clean vs ugly code, on tooling (build, front, back), dark / light background themes for your IDE, how do you format dates, and more.&lt;/p></description><content:encoded>
<![CDATA[<p>A few months ago, I had some fun doing the keynote of Voxxed Days Singapore and JUG Summer Camp, and I realized I didn&rsquo;t get a chance to share my deck. Furthermore, the videos of both events are actually available online: one in English, one in French!</p>
<p>I spoke about the so-called IT Holy Wars, you know, like Vi vs Emacs, Tabs &amp; Spaces, or other funny things of that kind. How developers circle from client-side to server-side, how we are polarized across strong positions on certain frameworks or practices, on clean vs ugly code, on tooling (build, front, back), dark / light background themes for your IDE, how do you format dates, and more.</p>
<p>I had lots of fun preparing and delivering this talk, both in English and French.</p>
<p>Let&rsquo;s start with English deck &amp; video:</p>
<script async class="speakerdeck-embed" data-id="d3cac7c15adb45e8bf10233754b04de9" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/4MDTBBEEyho?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>And now &ldquo;en français dans le texte&rdquo; : </p>
<script async class="speakerdeck-embed" data-id="f15e5f8b54544fd29408ca72dfd29885" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Build apps for voice, chat, web and mobile</title><link>https://glaforge.dev/talks/2018/02/15/build-apps-for-voice-chat-web-and-mobile/</link><pubDate>Thu, 15 Feb 2018 15:48:57 +0100</pubDate><guid>https://glaforge.dev/talks/2018/02/15/build-apps-for-voice-chat-web-and-mobile/</guid><description>&lt;p>&lt;strong>ServerlessConf Paris&lt;/strong> is in full swing this week, and I had the chance yesterday to participate in a workshop with my colleague Frank, to cover building apps for voice, chat, web and mobile, using Google&amp;rsquo;s serverless &lt;a href="https://developers.google.com/actions/">solutions. In particular, for voice &amp;amp; chat, I spoke about Dialogflow and Google Assistant / Actions&lt;/a> on Google, using &lt;a href="https://cloud.google.com/functions/">Cloud Functions&lt;/a> for my business logic, while in the afternoon we covered &lt;a href="https://firebase.google.com/">Firebase&lt;/a> un more depth.&lt;/p>
&lt;p>If you want to have a quick look at the deck, here are the slides that I presented:&lt;/p></description><content:encoded>
<![CDATA[<p><strong>ServerlessConf Paris</strong> is in full swing this week, and I had the chance yesterday to participate in a workshop with my colleague Frank, to cover building apps for voice, chat, web and mobile, using Google&rsquo;s serverless <a href="https://developers.google.com/actions/">solutions. In particular, for voice &amp; chat, I spoke about Dialogflow and Google Assistant / Actions</a> on Google, using <a href="https://cloud.google.com/functions/">Cloud Functions</a> for my business logic, while in the afternoon we covered <a href="https://firebase.google.com/">Firebase</a> un more depth.</p>
<p>If you want to have a quick look at the deck, here are the slides that I presented:</p>
<script async class="speakerdeck-embed" data-id="14c2dc2fe2d44413be0b4697469e4311" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>I'm a Java Champion!</title><link>https://glaforge.dev/posts/2018/01/18/i-m-a-java-champion/</link><pubDate>Thu, 18 Jan 2018 16:10:21 +0100</pubDate><guid>https://glaforge.dev/posts/2018/01/18/i-m-a-java-champion/</guid><description>&lt;p>The Java Champions recently nominated me to become a Java Champion!&lt;/p>
&lt;blockquote class="twitter-tweet">&lt;p lang="en" dir="ltr">Please welcome the new Java Champions. &lt;a href="https://twitter.com/antoine_sd?ref_src=twsrc%5Etfw">@antoine_sd&lt;/a> &lt;a href="https://twitter.com/javajudd?ref_src=twsrc%5Etfw">@javajudd&lt;/a> &lt;a href="https://twitter.com/glaforge?ref_src=twsrc%5Etfw">@glaforge&lt;/a> &lt;a href="https://twitter.com/kenkousen?ref_src=twsrc%5Etfw">@kenkousen&lt;/a> &lt;a href="https://twitter.com/kito99?ref_src=twsrc%5Etfw">@kito99&lt;/a> &lt;a href="https://twitter.com/vlad_mihalcea?ref_src=twsrc%5Etfw">@vlad_mihalcea&lt;/a>&lt;br>@leomrlima &lt;a href="https://twitter.com/net0pyr?ref_src=twsrc%5Etfw">@net0pyr&lt;/a> &lt;a href="https://twitter.com/shelajev?ref_src=twsrc%5Etfw">@shelajev&lt;/a> @rgransberger &lt;a href="https://twitter.com/rmehmandarov?ref_src=twsrc%5Etfw">@rmehmandarov&lt;/a>  &lt;a href="https://twitter.com/Sander_Mak?ref_src=twsrc%5Etfw">@Sander_Mak&lt;/a> &lt;a href="https://twitter.com/SeanMiPhillips?ref_src=twsrc%5Etfw">@SeanMiPhillips&lt;/a> Well done to all of you &lt;a href="https://twitter.com/OracleDevs?ref_src=twsrc%5Etfw">@OracleDevs&lt;/a> &lt;a href="https://twitter.com/java?ref_src=twsrc%5Etfw">@java&lt;/a> &lt;a href="https://twitter.com/hashtag/odevcommunity?src=hash&amp;amp;ref_src=twsrc%5Etfw">#odevcommunity&lt;/a> &lt;a href="https://t.co/X9yeek641s">pic.twitter.com/X9yeek641s&lt;/a>&lt;/p>&amp;mdash; Java Champions (@Java_Champions) &lt;a href="https://twitter.com/Java_Champions/status/933194279412891648?ref_src=twsrc%5Etfw">November 22, 2017&lt;/a>&lt;/blockquote>
&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8">&lt;/script>
&lt;p>There&amp;rsquo;s also a &lt;a href="https://blogs.oracle.com/java/new-java-champions-in-2017">post&lt;/a> on the Java Champions&amp;rsquo; blog, and &lt;a href="https://www.infoq.com/news/2018/01/JavaChampions2017">InfoQ&lt;/a> also echoed the new nominees recently.&lt;/p>
&lt;p>I&amp;rsquo;m super happy and proud of this nomination, and I&amp;rsquo;m looking forward to continuing being involved in the Java ecosystem, present at Java-friendly conferences, contribute to Open Source projects using Java and Apache Groovy, and write articles here and there using my favorite languages.&lt;/p></description><content:encoded>
<![CDATA[<p>The Java Champions recently nominated me to become a Java Champion!</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Please welcome the new Java Champions. <a href="https://twitter.com/antoine_sd?ref_src=twsrc%5Etfw">@antoine_sd</a> <a href="https://twitter.com/javajudd?ref_src=twsrc%5Etfw">@javajudd</a> <a href="https://twitter.com/glaforge?ref_src=twsrc%5Etfw">@glaforge</a> <a href="https://twitter.com/kenkousen?ref_src=twsrc%5Etfw">@kenkousen</a> <a href="https://twitter.com/kito99?ref_src=twsrc%5Etfw">@kito99</a> <a href="https://twitter.com/vlad_mihalcea?ref_src=twsrc%5Etfw">@vlad_mihalcea</a><br>@leomrlima <a href="https://twitter.com/net0pyr?ref_src=twsrc%5Etfw">@net0pyr</a> <a href="https://twitter.com/shelajev?ref_src=twsrc%5Etfw">@shelajev</a> @rgransberger <a href="https://twitter.com/rmehmandarov?ref_src=twsrc%5Etfw">@rmehmandarov</a>  <a href="https://twitter.com/Sander_Mak?ref_src=twsrc%5Etfw">@Sander_Mak</a> <a href="https://twitter.com/SeanMiPhillips?ref_src=twsrc%5Etfw">@SeanMiPhillips</a> Well done to all of you <a href="https://twitter.com/OracleDevs?ref_src=twsrc%5Etfw">@OracleDevs</a> <a href="https://twitter.com/java?ref_src=twsrc%5Etfw">@java</a> <a href="https://twitter.com/hashtag/odevcommunity?src=hash&amp;ref_src=twsrc%5Etfw">#odevcommunity</a> <a href="https://t.co/X9yeek641s">pic.twitter.com/X9yeek641s</a></p>&mdash; Java Champions (@Java_Champions) <a href="https://twitter.com/Java_Champions/status/933194279412891648?ref_src=twsrc%5Etfw">November 22, 2017</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


<p>There&rsquo;s also a <a href="https://blogs.oracle.com/java/new-java-champions-in-2017">post</a> on the Java Champions&rsquo; blog, and <a href="https://www.infoq.com/news/2018/01/JavaChampions2017">InfoQ</a> also echoed the new nominees recently.</p>
<p>I&rsquo;m super happy and proud of this nomination, and I&rsquo;m looking forward to continuing being involved in the Java ecosystem, present at Java-friendly conferences, contribute to Open Source projects using Java and Apache Groovy, and write articles here and there using my favorite languages.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Pre-trained machine learning APIs</title><link>https://glaforge.dev/talks/2018/01/17/pre-trained-machine-learning-apis/</link><pubDate>Wed, 17 Jan 2018 16:15:40 +0100</pubDate><guid>https://glaforge.dev/talks/2018/01/17/pre-trained-machine-learning-apis/</guid><description>&lt;p>Last month, for the first time, I visited Riga (Latvia), for the &lt;a href="https://devternity.com/">DevTernity conference&lt;/a>. I really enjoyed my time there, and wish to come back with other topics next time. The organizers took very well care of the speakers, and the presentations were very interesting.&lt;/p>
&lt;p>I had the pleasure to talk about the &lt;a href="https://cloud.google.com/products/machine-learning/">pre-trained machine learning APIs&lt;/a> provided by Google Cloud Platform, and say a few words as well about &lt;a href="https://www.tensorflow.org/">TensorFlow&lt;/a> and &lt;a href="https://cloud.google.com/ml-engine/">Cloud ML Engine&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>Last month, for the first time, I visited Riga (Latvia), for the <a href="https://devternity.com/">DevTernity conference</a>. I really enjoyed my time there, and wish to come back with other topics next time. The organizers took very well care of the speakers, and the presentations were very interesting.</p>
<p>I had the pleasure to talk about the <a href="https://cloud.google.com/products/machine-learning/">pre-trained machine learning APIs</a> provided by Google Cloud Platform, and say a few words as well about <a href="https://www.tensorflow.org/">TensorFlow</a> and <a href="https://cloud.google.com/ml-engine/">Cloud ML Engine</a>.</p>
<p>The talk was recorded, and I wanted to share with you the video and the slides of the presentation.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/_5SmyANkq3o?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>And here is the deck:</p>
<script async class="speakerdeck-embed" data-id="c2927b99b7b64b9c83a90fe81704a9f2" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>The 2018 countdown: a tip a day about Google Cloud Platform</title><link>https://glaforge.dev/posts/2017/12/18/the-2018-countdown-a-tip-a-day-about-google-cloud-platform/</link><pubDate>Mon, 18 Dec 2017 16:22:19 +0100</pubDate><guid>https://glaforge.dev/posts/2017/12/18/the-2018-countdown-a-tip-a-day-about-google-cloud-platform/</guid><description>&lt;p>A few weeks ago, I&amp;rsquo;ve started a new blog dedicated to &lt;a href="https://cloud.google.com/">Google Cloud Platform&lt;/a>, to share tips&amp;rsquo;n tricks I come across while exploring the platform, getting to know new products, or gathered through experience with a particular service I&amp;rsquo;ve been using:&lt;/p>
&lt;p>&lt;a href="https://googlecloud.tips/">https://googlecloud.tips/&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://googlecloud.tips/">&lt;figure>
&lt;a href="#img-4ea7433448ab0864c97c21021fae77ff">
&lt;img src="https://glaforge.dev/img/gcp-tips/gcp-tips-frontpage.jpg"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-4ea7433448ab0864c97c21021fae77ff">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/gcp-tips/gcp-tips-frontpage.jpg"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/a>&lt;/p>
&lt;p>With the holidays season, I went with a &amp;ldquo;2018 countdown&amp;rdquo; approach (like an &amp;ldquo;advent calendar&amp;rdquo; without the religious connotation), where I publish a tip every day of the month of December.&lt;/p></description><content:encoded>
<![CDATA[<p>A few weeks ago, I&rsquo;ve started a new blog dedicated to <a href="https://cloud.google.com/">Google Cloud Platform</a>, to share tips&rsquo;n tricks I come across while exploring the platform, getting to know new products, or gathered through experience with a particular service I&rsquo;ve been using:</p>
<p><a href="https://googlecloud.tips/">https://googlecloud.tips/</a></p>
<p><a href="https://googlecloud.tips/"><figure>
  <a href="#img-4ea7433448ab0864c97c21021fae77ff">
    <img src="/img/gcp-tips/gcp-tips-frontpage.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-4ea7433448ab0864c97c21021fae77ff">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gcp-tips/gcp-tips-frontpage.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</a></p>
<p>With the holidays season, I went with a &ldquo;2018 countdown&rdquo; approach (like an &ldquo;advent calendar&rdquo; without the religious connotation), where I publish a tip every day of the month of December.</p>
<p>As of today, December 18th, we already have 18 tips available!</p>
<p>Those tips span about a dozen technologies! (as you can also explore the tips via &ldquo;tags&rdquo;, which represent a technology / service / API / product each)</p>
<p><figure>
  <a href="#img-a187ac9487c184c6aacf737a025e3d5f">
    <img src="/img/gcp-tips/gcptips-tags.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-a187ac9487c184c6aacf737a025e3d5f">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gcp-tips/gcptips-tags.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Initially, I thought it would be a challenge to author tips every day (without even thinking of the fact you have to publish tips during weekends, holidays or vacations), but there are actually plenty of tricks to share. Furthermore, I opened the blog to contributions: anyone who wants to contribute a tip or two is welcome, and should just share with me a quick gist describing the tip. I&rsquo;ve already received a bunch of contributions, from 8 distinct authors. Thanks a lot to Alexandre, Bastien, Fabien, Graham, Jim, Mark, Victor, or Wassim!</p>
<p>Although it all started as a tip a day for the 2018 countdown, it won&rsquo;t stop there. Perhaps the frequency will be a bit lower (once a week? more?), but I definitely intend on continuing sharing tips on a regular basis next year and beyond!</p>
<p>If you want to help, please spread the word! Tell your friends and colleagues about the site: <a href="https://googlecloud.tips/">https://googlecloud.tips/</a>.</p>
<p>Also please follow the <a href="https://twitter.com/GcpTips">@gcptips</a> Twitter account where new tips&rsquo;n tricks are announced.</p>
<p>And if you&rsquo;ve got some time, don&rsquo;t hesitate to share your own tips! All help is welcome :-)</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Gradle vs Maven and Gradle in Kotlin or Groovy</title><link>https://glaforge.dev/posts/2017/11/27/gradle-vs-maven-and-gradle-in-kotlin-or-groovy/</link><pubDate>Mon, 27 Nov 2017 16:29:35 +0100</pubDate><guid>https://glaforge.dev/posts/2017/11/27/gradle-vs-maven-and-gradle-in-kotlin-or-groovy/</guid><description>&lt;p>Once in a while, when talking about Gradle with developers, at conferences or within the Groovy community (but with the wider Java community as well), I hear questions about Gradle. In particular Gradle vs Maven, or whether developers adopt the Kotlin DSL for Gradle builds.&lt;/p>
&lt;p>In the past, I blogged several times about using BigQuery and the Github dataset to analyze open source projects hosted on Github, by running some SQL queries against that dataset. You might want to have a look at this past article on some &lt;a href="https://glaforge.dev/posts/2016/12/03/analyzing-half-a-million-gradle-build-files/">Gradle analysis with BigQuery&lt;/a>. Considering those questions popped up recently, I decided to do a quick run through those questions with some simple queries.&lt;/p></description><content:encoded>
<![CDATA[<p>Once in a while, when talking about Gradle with developers, at conferences or within the Groovy community (but with the wider Java community as well), I hear questions about Gradle. In particular Gradle vs Maven, or whether developers adopt the Kotlin DSL for Gradle builds.</p>
<p>In the past, I blogged several times about using BigQuery and the Github dataset to analyze open source projects hosted on Github, by running some SQL queries against that dataset. You might want to have a look at this past article on some <a href="https://glaforge.dev/posts/2016/12/03/analyzing-half-a-million-gradle-build-files/">Gradle analysis with BigQuery</a>. Considering those questions popped up recently, I decided to do a quick run through those questions with some simple queries.</p>
<h2 id="gradle-vs-maven">Gradle vs Maven?</h2>
<p>First, let&rsquo;s look at Maven builds. We can run the following query:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span>(<span style="color:#666">*</span>)<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[bigquery<span style="color:#666">-</span><span style="color:#007020;font-weight:bold">public</span><span style="color:#666">-</span><span style="color:#007020;font-weight:bold">data</span>:github_repos.files]<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">WHERE</span><span style="color:#bbb"> </span>path<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">LIKE</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#39;%pom.xml&#39;</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>There are 1,125,150 pom files.</p>
<p><figure>
  <a href="#img-43a17394b8f79d4c02230ddb6d395693">
    <img src="/img/bq-groovy/gradle-builds-vs-maven-builds.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-43a17394b8f79d4c02230ddb6d395693">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bq-groovy/gradle-builds-vs-maven-builds.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Then, for Gradle, I ran this query (even if projects could have different build file names):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span>(<span style="color:#666">*</span>)<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[bigquery<span style="color:#666">-</span><span style="color:#007020;font-weight:bold">public</span><span style="color:#666">-</span><span style="color:#007020;font-weight:bold">data</span>:github_repos.files]<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">WHERE</span><span style="color:#bbb"> </span>path<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">LIKE</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#39;%build.gradle&#39;</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>There are 414,329 build.gradle files.</p>
<p><figure>
  <a href="#img-bc8f9c55b06ad52009b95237c8ec0c59">
    <img src="/img/bq-groovy/gradle-builds-in-groovy.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-bc8f9c55b06ad52009b95237c8ec0c59">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bq-groovy/gradle-builds-in-groovy.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>So that&rsquo;s 1 Gradle build file for 2.7 Maven build file.</p>
<h2 id="gradle-builds-in-kotlin-or-in-groovy">Gradle builds in Kotlin or in Groovy?</h2>
<p>Now for Kotlin, the convention seems to be about naming your build files with build.gradle.kts. So let&rsquo;s run the following query:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span>(<span style="color:#666">*</span>)<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[bigquery<span style="color:#666">-</span><span style="color:#007020;font-weight:bold">public</span><span style="color:#666">-</span><span style="color:#007020;font-weight:bold">data</span>:github_repos.files]<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">WHERE</span><span style="color:#bbb"> </span>path<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">LIKE</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#39;%build.gradle.kts&#39;</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>There are only 207 Gradle builds files written in Kotlin.</p>
<p><figure>
  <a href="#img-aad7cc7344be01350828e90f9e0b1d1b">
    <img src="/img/bq-groovy/gradle-builds-in-kotlin.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-aad7cc7344be01350828e90f9e0b1d1b">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bq-groovy/gradle-builds-in-kotlin.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Basically, Groovy-based Gradle builds are 2000 times more popular than Kotlin-based builds.</p>
<h2 id="a-grain-of-salt">A grain of salt</h2>
<p>Now, all that said, remember that developers can name their build files differently, that it&rsquo;s only a snapshot of the projects available on Github, and furthermore, just open source projects (at least projects that explicitly have a LICENSE file). Note for example as well that there are Gradle based projects that also have a pom.xml file available, although they&rsquo;re not using Maven for their build.</p>
<p>Also, perhaps it&rsquo;d be more interesting to run the queries by counting repositories, rather than build files: Perhaps Gradle users tend to split their build files in smaller build files, in a less monolithic way than with Gradle? Practices and habits may vary greatly.</p>
<p>For the Gradle vs Maven question, at Devoxx Belgium, I ran the following (more complex) query where I look at repositories containing Gradle or Maven build files:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#666">#</span>standardSQL<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">select</span><span style="color:#bbb"> </span>file,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span>(file)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>(<span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#39;gradle&#39;</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">as</span><span style="color:#bbb"> </span>file,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span>(repo_name)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span><span style="color:#666">`</span>bigquery<span style="color:#666">-</span><span style="color:#007020;font-weight:bold">public</span><span style="color:#666">-</span><span style="color:#007020;font-weight:bold">data</span>.github_repos.files<span style="color:#666">`</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">WHERE</span><span style="color:#bbb"> </span>path<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">LIKE</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#39;%build.gradle&#39;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">GROUP</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">BY</span><span style="color:#bbb"> </span>repo_name)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">UNION</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">ALL</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>(<span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#34;maven&#34;</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">as</span><span style="color:#bbb"> </span>file,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span>(repo_name)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span><span style="color:#666">`</span>bigquery<span style="color:#666">-</span><span style="color:#007020;font-weight:bold">public</span><span style="color:#666">-</span><span style="color:#007020;font-weight:bold">data</span>.github_repos.files<span style="color:#666">`</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">WHERE</span><span style="color:#bbb"> </span>path<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">LIKE</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#39;%pom.xml&#39;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">GROUP</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">BY</span><span style="color:#bbb"> </span>repo_name)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">group</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">by</span><span style="color:#bbb"> </span>file<span style="color:#bbb">
</span></span></span></code></pre></div><p>Gradle and Maven are already much closer to each other by looking at repository counts than just by pure number of build files, perhaps indeed showing a trend with Gradle users to modularize their builds more.</p>
<p>We get 118,386 repositories using Gradle versus 143,290 repositories using Maven. So Gradle is almost at the same level as Maven from that repository perspective, Still catching up with Maven!</p>
<h2 id="famous-last-words">Famous last words</h2>
<p>Don&rsquo;t necessarily draw too big conclusions out of those figures, there are many ways to make stats, and those figures are only a small fraction of all the projects in existence in the world&hellip; but at least, they certainly exhibit a certain trend, which is still interesting to know and think about!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>The JDK built-in web server with Apache Groovy</title><link>https://glaforge.dev/posts/2017/11/20/the-jdk-builtin-web-server-with-apache-groovy/</link><pubDate>Mon, 20 Nov 2017 16:38:29 +0100</pubDate><guid>https://glaforge.dev/posts/2017/11/20/the-jdk-builtin-web-server-with-apache-groovy/</guid><description>&lt;p>In my timeline, I saw a tweet from Joe Walnes about the built-in HTTP server available in the JDK since Java 6. It&amp;rsquo;s super convenient, starts super fast, easy to use, but I often forget about it. I&amp;rsquo;d probably not use it for serving planet-wide load, but it&amp;rsquo;s very useful when you need to create a quick service, a little mock for testing some web or micro-service.&lt;/p>
&lt;p>Here&amp;rsquo;s a little hello world for the fun.&lt;/p></description><content:encoded>
<![CDATA[<p>In my timeline, I saw a tweet from Joe Walnes about the built-in HTTP server available in the JDK since Java 6. It&rsquo;s super convenient, starts super fast, easy to use, but I often forget about it. I&rsquo;d probably not use it for serving planet-wide load, but it&rsquo;s very useful when you need to create a quick service, a little mock for testing some web or micro-service.</p>
<p>Here&rsquo;s a little hello world for the fun.</p>
<p>I&rsquo;m taking advantage of Apache Groovy&rsquo;s closure-to-functional-interface coercion support, as well as the <code>with{}</code> method to reuse the <code>HttpServer</code> instance for two method calls on the same instance (I could&rsquo;ve used it for the <code>http</code> variable as well, actually).</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">com.sun.net.httpserver.HttpServer</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>HttpServer<span style="color:#666">.</span><span style="color:#4070a0">create</span><span style="color:#666">(</span><span style="color:#007020;font-weight:bold">new</span> InetSocketAddress<span style="color:#666">(</span><span style="color:#40a070">8080</span><span style="color:#666">),</span> <span style="color:#40a070">0</span><span style="color:#666">).</span><span style="color:#4070a0">with</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    createContext<span style="color:#666">(</span><span style="color:#4070a0">&#34;/hello&#34;</span><span style="color:#666">)</span> <span style="color:#666">{</span> http <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>        http<span style="color:#666">.</span><span style="color:#4070a0">responseHeaders</span><span style="color:#666">.</span><span style="color:#4070a0">add</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;Content-type&#34;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#34;text/plain&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        http<span style="color:#666">.</span><span style="color:#4070a0">sendResponseHeaders</span><span style="color:#666">(</span><span style="color:#40a070">200</span><span style="color:#666">,</span> <span style="color:#40a070">0</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        http<span style="color:#666">.</span><span style="color:#4070a0">responseBody</span><span style="color:#666">.</span><span style="color:#4070a0">withWriter</span> <span style="color:#666">{</span> out <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>            out <span style="color:#666">&lt;&lt;</span> <span style="color:#4070a0">&#34;Hello ${http.remoteAddress.hostName}!&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    start<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>More voice control for Actions on Google</title><link>https://glaforge.dev/posts/2017/11/16/more-voice-control-for-actions-on-google/</link><pubDate>Thu, 16 Nov 2017 16:40:32 +0100</pubDate><guid>https://glaforge.dev/posts/2017/11/16/more-voice-control-for-actions-on-google/</guid><description>&lt;p>Today, there were some interesting &lt;a href="https://developers.googleblog.com/2017/11/help-users-find-interact-re-engage-with.html">announcements for Actions on Google&lt;/a>, for building your conversational interfaces for the &lt;a href="https://assistant.google.com/">Google Assistant&lt;/a>. Among the great news, one item particularly caught my attention: the improved SSML support:&lt;/p>
&lt;link rel="stylesheet" href="https://glaforge.dev/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&amp;#43;AD&amp;#43;9EeYXUTm&amp;#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
&lt;div class="admonition info">
&lt;div class="admonition-header">&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">&lt;path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/>&lt;/svg>
&lt;span>Better SSML&lt;/span>
&lt;/div>
&lt;div class="admonition-content">
&lt;p>We recently rolled out an update to the web simulator
which includes a new SSML audio design experience.
We now give you more options for creating natural,
quality dialog using newly supported SSML tags, including &amp;lt;prosody&amp;gt;,
&amp;lt;emphasis&amp;gt;, &amp;lt;audio&amp;gt; and others. The new tag &amp;lt;par&amp;gt; is coming soon
and lets you add mood and richness, so you can play background music
and ambient sounds while a user is having a conversation with your app.
To help you get started, we&amp;rsquo;ve added over 1,000 sounds to the sound library.
Listen to a brief SSML audio experiment that shows off some of the new features here.&lt;/p></description><content:encoded>
<![CDATA[<p>Today, there were some interesting <a href="https://developers.googleblog.com/2017/11/help-users-find-interact-re-engage-with.html">announcements for Actions on Google</a>, for building your conversational interfaces for the <a href="https://assistant.google.com/">Google Assistant</a>. Among the great news, one item particularly caught my attention: the improved SSML support:</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Better SSML</span>
      </div>
      <div class="admonition-content">
        <p>We recently rolled out an update to the web simulator
which includes a new SSML audio design experience.
We now give you more options for creating natural,
quality dialog using newly supported SSML tags, including &lt;prosody&gt;,
&lt;emphasis&gt;, &lt;audio&gt; and others. The new tag &lt;par&gt; is coming soon
and lets you add mood and richness, so you can play background music
and ambient sounds while a user is having a conversation with your app.
To help you get started, we&rsquo;ve added over 1,000 sounds to the sound library.
Listen to a brief SSML audio experiment that shows off some of the new features here.</p>
      </div>
    </div><p>SSML stands for <a href="https://www.w3.org/TR/speech-synthesis/">Speech Synthesis Markup Language</a>. It&rsquo;s a W3C standard whose goal is to provide better support for a more natural sounding speech generation.</p>
<p>So far, <a href="https://developers.google.com/actions/">Actions on Google</a> had limited SSML support, but today, there&rsquo;s a bit more you can do with SSML to enhance your apps&rsquo; voice!</p>
<p>At the Devoxx Belgium conference last week, in a couple of talks showing Dialogflow, </p>
<p>Actions on Google, and Cloud Functions, I showed some quick examples of SSML.</p>
<p>For example, I <a href="https://youtu.be/7NjRqMYH11s?t=40m41s">made an attendee do some squats on stage</a>! (but the camera didn&rsquo;t catch that unfortunately.) I created a loop over a tick-tock sound to mimick a countdown. I repeated x times the tick-tock sound. With x audio elements. But we can do better now, by using the repeatCount attribute instead!</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;audio</span> <span style="color:#4070a0">src=</span><span style="color:#4070a0">&#34;gs://my-bucket-sounds/tick-tock-1s.wav&#34;</span> <span style="color:#4070a0">repeatCount=</span><span style="color:#4070a0">&#34;10&#34;</span> <span style="color:#062873;font-weight:bold">/&gt;</span>
</span></span></code></pre></div><p>It&rsquo;s much better than repeating my audio tag 10 times!</p>
<p>If you want to make your interactions even more lively, you could already use the Actions on Google <a href="https://developers.google.com/actions/tools/sound-library/">sound library</a>, or use a free sound library like <a href="https://www.freesound.org/">Freesound</a>.</p>
<p>But there&rsquo;s a promising upcoming tag that&rsquo;s gonna be supported soon: &lt;par/&gt;</p>
<p>If you will, par is a bit like a multi-track audio mixer. You&rsquo;ll be able to play different sounds in parallel, or make the voice speak in parallel. So you could very well have a background sound or music, with your app speaking at the same time.</p>
<p>Speaking of voice, the human voice goes up and down in pitch. With the prosody element, you can define the rate, pitch, and volume attributes. For instance, I make my voice sing some notes with semitones (but to be honest, it doesn&rsquo;t quite sound yet like a real singer!)</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;speak&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;prosody</span> <span style="color:#4070a0">rate=</span><span style="color:#4070a0">&#34;slow&#34;</span> <span style="color:#4070a0">pitch=</span><span style="color:#4070a0">&#34;-7st&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>C<span style="color:#062873;font-weight:bold">&lt;/prosody&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;prosody</span> <span style="color:#4070a0">rate=</span><span style="color:#4070a0">&#34;slow&#34;</span> <span style="color:#4070a0">pitch=</span><span style="color:#4070a0">&#34;-5st&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>D<span style="color:#062873;font-weight:bold">&lt;/prosody&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;prosody</span> <span style="color:#4070a0">rate=</span><span style="color:#4070a0">&#34;slow&#34;</span> <span style="color:#4070a0">pitch=</span><span style="color:#4070a0">&#34;-3st&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>E<span style="color:#062873;font-weight:bold">&lt;/prosody&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;prosody</span> <span style="color:#4070a0">rate=</span><span style="color:#4070a0">&#34;slow&#34;</span> <span style="color:#4070a0">pitch=</span><span style="color:#4070a0">&#34;-2st&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>F<span style="color:#062873;font-weight:bold">&lt;/prosody&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;prosody</span> <span style="color:#4070a0">rate=</span><span style="color:#4070a0">&#34;slow&#34;</span> <span style="color:#4070a0">pitch=</span><span style="color:#4070a0">&#34;0st&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>G<span style="color:#062873;font-weight:bold">&lt;/prosody&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;prosody</span> <span style="color:#4070a0">rate=</span><span style="color:#4070a0">&#34;slow&#34;</span> <span style="color:#4070a0">pitch=</span><span style="color:#4070a0">&#34;+2st&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>A<span style="color:#062873;font-weight:bold">&lt;/prosody&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;prosody</span> <span style="color:#4070a0">rate=</span><span style="color:#4070a0">&#34;slow&#34;</span> <span style="color:#4070a0">pitch=</span><span style="color:#4070a0">&#34;+4st&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>B<span style="color:#062873;font-weight:bold">&lt;/prosody&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;prosody</span> <span style="color:#4070a0">rate=</span><span style="color:#4070a0">&#34;slow&#34;</span> <span style="color:#4070a0">pitch=</span><span style="color:#4070a0">&#34;+6st&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>C<span style="color:#062873;font-weight:bold">&lt;/prosody&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/speak&gt;</span>
</span></span></code></pre></div><p>You can also play with different levels of emphasis:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;speak&gt;</span>
</span></span><span style="display:flex;"><span>  This is 
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;emphasis</span> <span style="color:#4070a0">level=</span><span style="color:#4070a0">&#34;strong&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>really<span style="color:#062873;font-weight:bold">&lt;/emphasis&gt;</span>
</span></span><span style="display:flex;"><span>  cool!
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/speak&gt;</span>
</span></span></code></pre></div><p>Learn more about all the <a href="https://developers.google.com/actions/reference/ssml">support SSML tags in the Actions on Google documentation</a>! It&rsquo;s gonna be even more fun to create lively voice interactions with all those improvements!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>JavaOne — How languages influence each other: Reflections on 14 years of Apache Groovy</title><link>https://glaforge.dev/talks/2017/10/09/javaone-how-languages-influence-each-other-reflections-on-14-years-of-apache-groovy/</link><pubDate>Mon, 09 Oct 2017 16:42:54 +0100</pubDate><guid>https://glaforge.dev/talks/2017/10/09/javaone-how-languages-influence-each-other-reflections-on-14-years-of-apache-groovy/</guid><description>&lt;p>Last week, I was in San Francisco for my tenth JavaOne! I had two sessions: one on the past / present / future of Java Platform-as-a-Service offerings, and one on programming language influences, and particularly how was Apache Groovy influenced, and how it also inspired other languages.&lt;/p>
&lt;p>Here&amp;rsquo;s the abstract:&lt;/p>
&lt;blockquote>
&lt;p>Languages have been influencing one another since the dawn of computer programming. There are families of languages: from Algol descendants with begin/end code blocks to those with curly braces such as C. Languages are not invented in a vacuum but are inspired by their predecessors. This session&amp;rsquo;s speaker, who has been working on Apache Groovy for the past 14 years, reflects on the influences that have driven the design of programming languages. In particular, Groovy&amp;rsquo;s base syntax was directly derived from Java&amp;rsquo;s but quickly developed its own flavor, adding closures, type inference, and operators from Ruby. Groovy also inspired other languages: C#, Swift, and JavaScript adopted Groovy&amp;rsquo;s null-safe navigation operator and the famous Elvis operator.&lt;/p></description><content:encoded>
<![CDATA[<p>Last week, I was in San Francisco for my tenth JavaOne! I had two sessions: one on the past / present / future of Java Platform-as-a-Service offerings, and one on programming language influences, and particularly how was Apache Groovy influenced, and how it also inspired other languages.</p>
<p>Here&rsquo;s the abstract:</p>
<blockquote>
<p>Languages have been influencing one another since the dawn of computer programming. There are families of languages: from Algol descendants with begin/end code blocks to those with curly braces such as C. Languages are not invented in a vacuum but are inspired by their predecessors. This session&rsquo;s speaker, who has been working on Apache Groovy for the past 14 years, reflects on the influences that have driven the design of programming languages. In particular, Groovy&rsquo;s base syntax was directly derived from Java&rsquo;s but quickly developed its own flavor, adding closures, type inference, and operators from Ruby. Groovy also inspired other languages: C#, Swift, and JavaScript adopted Groovy&rsquo;s null-safe navigation operator and the famous Elvis operator.</p></blockquote>
<p>And you can have a look at the slides below:</p>
<script async class="speakerdeck-embed" data-id="2159d9f06d0b4b388a849823f43e82fc" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p><a href="http://www.groovy-lang.org/">Apache Groovy</a> is a multi-faceted language for the Java platform, allowing developers to code in a Java-friendly syntax, with great integration with the Java ecosystem, and powerful scripting and Domain-Specific Language capabilities, while at the same time being able to offer you type safety and static compilation.</p>
<p>In this presentation, I revisited some of the influences from other languages, from the C-family and its Java older brother, going through its Python-inspired strings, its Smalltalk and Ruby heritage for named parameters and closures, its type system à-la-Java. But I&rsquo;m also showing some of the innovations Groovy came up with that were later borrowed by others (Swift, C#, Kotlin, Ceylon, PHP, Ruby, Coffeescript&hellip;). Things like Groovy&rsquo;s trailing closure, Groovy builders, null-safe navigation, the Elvis operator, ranges, the spaceship operator, and more.</p>
<p>Ultimately, inspiration is really a two-way street, as languages don&rsquo;t come from nowhere and inherit from their older brothers and sisters. No language is perfect, but each one of them somehow help the next ones to get better, by borrowing here and there some nice features that make developers more productive and write more readable and maintainable code.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Building your own chatbots with API.ai and Cloud Functions</title><link>https://glaforge.dev/talks/2017/10/08/building-your-own-chatbots-with-apiai-and-cloud-functions/</link><pubDate>Sun, 08 Oct 2017 16:56:13 +0100</pubDate><guid>https://glaforge.dev/talks/2017/10/08/building-your-own-chatbots-with-apiai-and-cloud-functions/</guid><description>&lt;p>A few weeks ago, my buddy &lt;a href="https://twitter.com/manekinekko">Wassim&lt;/a> and I had the chance to present again on the topic of chatbots, with API.AI and Cloud Functions, at the &lt;a href="https://devfesttoulouse.fr/">DevFest Toulouse&lt;/a> conference.&lt;/p>
&lt;p>Here&amp;rsquo;s the latest update to our slide deck:&lt;/p>
&lt;script async class="speakerdeck-embed" data-id="1b47c1e6bb7c4f81b5e0237cbfbda1ca" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js">&lt;/script>
&lt;p>There&amp;rsquo;s also a video (in French) of the same content from Devoxx France, where I was showing how to build a conference chatbot:&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/vNoiTnnlGC0?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video">&lt;/iframe>
&lt;/div>
&lt;p>Chatbots, per se, are not really new, in the sense that we&amp;rsquo;ve been developing bots for things like IRC for a long time, but back in the day, it was simply some regular expression labor of love, rather than the natural language that we use today. The progress in machine learning, in both speech recognition (for when you use devices like Google Home) and natural language understanding (NLU), is what led us to being able to speak and chat naturally to those chatbots we encounter now.&lt;/p></description><content:encoded>
<![CDATA[<p>A few weeks ago, my buddy <a href="https://twitter.com/manekinekko">Wassim</a> and I had the chance to present again on the topic of chatbots, with API.AI and Cloud Functions, at the <a href="https://devfesttoulouse.fr/">DevFest Toulouse</a> conference.</p>
<p>Here&rsquo;s the latest update to our slide deck:</p>
<script async class="speakerdeck-embed" data-id="1b47c1e6bb7c4f81b5e0237cbfbda1ca" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>There&rsquo;s also a video (in French) of the same content from Devoxx France, where I was showing how to build a conference chatbot:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/vNoiTnnlGC0?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>Chatbots, per se, are not really new, in the sense that we&rsquo;ve been developing bots for things like IRC for a long time, but back in the day, it was simply some regular expression labor of love, rather than the natural language that we use today. The progress in machine learning, in both speech recognition (for when you use devices like Google Home) and natural language understanding (NLU), is what led us to being able to speak and chat naturally to those chatbots we encounter now.</p>
<p>In this presentation, we&rsquo;re covering the key concepts that underpin the NLU aspects:</p>
<ul>
<li><strong>Intents</strong> &mdash; the various kind of sentences or actions that are recognized (ex: &ldquo;I-want-to-eat-something&rdquo;)</li>
<li><strong>Entities</strong> &mdash; the concepts and values that we manipulate or that are parameterizing intents (ex: the kind of food associated with the &ldquo;I-want-to-eat-something&rdquo; intent)</li>
<li><strong>Context</strong> &mdash; a conversation is not just a request-reply exchange, but the discussion between you and the chatbot can span longer back&rsquo;n forth exchanges, and the chatbot needs to remember what was previously said to be useful and avoid any frustration for the user</li>
</ul>
<p>We&rsquo;re also clarifying some of the terminology used when working with the Google Assistant and its Actions on Google developer platform:</p>
<ul>
<li><strong>Google Assistant</strong> &mdash; a conversation between you and Google to help GTD</li>
<li><strong>Google Home</strong> &mdash; voice activated speaker powered by the Google Assistant</li>
<li><strong>Google Assistant SDK</strong> &mdash; kit to embed the Google Assistant in your devices</li>
<li><strong>Agent / chatbot / action</strong> &mdash; an actual app serving a particular purpose</li>
<li><strong>Actions on Google</strong> &mdash; developer platform to build apps for the Assistant</li>
<li><strong>Apps for the Google Assistant</strong> &mdash; 3rd party apps integrated to the Assistant</li>
<li><strong>Actions SDK</strong> &mdash; a software SDK for creating apps</li>
<li><strong>API.AI</strong> (now called <strong>DialogFlow</strong>) &mdash; a platform for creating conversational interfaces</li>
</ul>
<p>It&rsquo;s important that your chatbot has a consistent persona, that corresponds to the core values or attributes of your brand, the spirit of your bot. A bot for children will likely be more friendly and use easy to understand vocabulary, vs a more formal tone for, say, a bank chatbot).</p>
<p>There are some great resources available for seeing if your chatbot and its conversation is ready for prime time:</p>
<ul>
<li><a href="http://g.co/dev/ActionsChecklist">g.co/dev/ActionsChecklist</a> &mdash; a checklist with various aspects to double check</li>
<li><a href="http://g.co/dev/ActionsDesign">g.co/dev/ActionsDesign</a> &mdash; several useful guides explaining how proper human conversation work</li>
</ul>
<p>Our tool of choice for our demo is <a href="https://api.ai/">API.AI</a>, for implementing the voice interactions. It&rsquo;s clearly one of the best platforms on the market that makes it simple to create intents, entities, handle contexts, deal with many predefined entity types, that also provides various pre-built conversations that you can peruse.</p>
<p>For the business logic, we went with <a href="https://cloud.google.com/functions/">Google Cloud Functions</a> which allows us to define our logic using JavaScript and Node.JS. We also took advantage of the local <a href="https://cloud.google.com/functions/docs/emulator">Cloud Functions emulator</a>, to run our logic on our local machine, and <a href="https://ngrok.com/">ngrok</a> for creating a tunnel between that local machine and API.AI. In API.AI, in the fulfillment webhook, you&rsquo;ll put the temporary URL given by ngrok, that then points at your local machine, via ngrok&rsquo;s tunnel. That way, you can see changes immediately, thanks to the live reloading supported by the emulator, making it easy to evolve your code.</p>
<p>Cloud Functions is Google&rsquo;s function-as-a-service offering, which is a serverless service, taylored for event-oriented systems as well as for direct HTTP invocation, and you pay only as you go, as requests are made or events are sent to your function. It&rsquo;s a cost effective solution, that scale automatically with your load.</p>
<p>To finish, we&rsquo;re also saying a few words about how to submit your bot to the Actions on Google development platform, to extend the Google Assistant with your own ideas.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Cloud Shell and its Orion-based text editor to develop in the cloud</title><link>https://glaforge.dev/posts/2017/08/07/cloud-shell-and-its-orion-based-text-editor-to-develop-in-the-cloud/</link><pubDate>Mon, 07 Aug 2017 23:42:11 +0100</pubDate><guid>https://glaforge.dev/posts/2017/08/07/cloud-shell-and-its-orion-based-text-editor-to-develop-in-the-cloud/</guid><description>&lt;p>After deploying in the cloud, there&amp;rsquo;s a new trend towards programming in the cloud. Although I&amp;rsquo;m not sure we&amp;rsquo;re quite there yet, there are a couple of handy tools I&amp;rsquo;ve been enjoying when working on the &lt;a href="https://cloud.google.com/">Google Cloud Platform&lt;/a>.&lt;/p>
&lt;p>I had been using the built-in &lt;a href="https://cloud.google.com/shell/">Cloud Shell&lt;/a> console, on the Google Cloud console, to have a terminal already pre-configured for my Google Cloud project. It allows you to easily have access to your whole environment, run commands, etc, just like you would from your own computer. The fact that all the command-line tools you can imagine (gradle, maven, gcloud sdk, etc) are already there is helpful, as well as the fact that you are already configured for using other cloud services.&lt;/p></description><content:encoded>
<![CDATA[<p>After deploying in the cloud, there&rsquo;s a new trend towards programming in the cloud. Although I&rsquo;m not sure we&rsquo;re quite there yet, there are a couple of handy tools I&rsquo;ve been enjoying when working on the <a href="https://cloud.google.com/">Google Cloud Platform</a>.</p>
<p>I had been using the built-in <a href="https://cloud.google.com/shell/">Cloud Shell</a> console, on the Google Cloud console, to have a terminal already pre-configured for my Google Cloud project. It allows you to easily have access to your whole environment, run commands, etc, just like you would from your own computer. The fact that all the command-line tools you can imagine (gradle, maven, gcloud sdk, etc) are already there is helpful, as well as the fact that you are already configured for using other cloud services.</p>
<p>To launch the shell, look no further than the top right hand corner, and click on the little [&gt;_] button. It will launch the terminal in the bottom part of your cloud console.</p>
<p><figure>
  <a href="#img-bcd12677814b2fffaaac0866ca0b6430">
    <img src="/img/cloud-shell/cloud-shell-launch.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-bcd12677814b2fffaaac0866ca0b6430">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/cloud-shell/cloud-shell-launch.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
 </p>
<p>You will see the console popping up below, and you&rsquo;ll be ready to access your project&rsquo;s environment:</p>
<p><figure>
  <a href="#img-63d2860ee51fef74093574c0a7c0f098">
    <img src="/img/cloud-shell/cloud-shell-terminal.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-63d2860ee51fef74093574c0a7c0f098">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/cloud-shell/cloud-shell-terminal.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>But look at this little pen icon above? If you click it, you&rsquo;ll get your terminal in full screen in another window, but more interestingly, it will launch a proper file editor! It&rsquo;s an editor based on <a href="https://orionhub.org/">Eclipse Orion&rsquo;s web editor</a>. You have your usual file browsing pane, to navigate to and select which files you want to edit, and you also have things like syntax highlighting to better understand the code at hand.</p>
<p><figure>
  <a href="#img-c9cca44ec3a38af869d8521c110af274">
    <img src="/img/cloud-shell/cloud-shell-editor.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-c9cca44ec3a38af869d8521c110af274">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/cloud-shell/cloud-shell-editor.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>The more friendly those built-in web editors will become, the sooner we&rsquo;ll really be able to develop in the cloud. I believe I will still continue to work on my local computer a long, but there are already times when I prefer running some operations directly in the cloud: for example, tasks that are really network hungry, they benefit directly from the wonderful network that cloud shell has access to, which is much snappier than the connection I have at home on my DSL router. For example, running some Docker build command, or fetching tons of dependencies for Node or Maven/Gradle, and it&rsquo;s really much nicer and faster within Cloud Shell. So having the added capability of also editing some files in my project make things pretty snappy.</p>
<p>There was a recent article on the Google Cloud blog outlining the <a href="https://cloudplatform.googleblog.com/2017/07/Cloud-Shells-code-editor-now-in-beta.html">beta launch of the Cloud Shell&rsquo;s code editor</a>, which is why I wanted to play with this new built-in editor.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Apache Groovy and Google App Engine at JavaOne</title><link>https://glaforge.dev/posts/2017/07/21/apache-groovy-and-google-app-engine-at-javaone/</link><pubDate>Fri, 21 Jul 2017 23:45:09 +0100</pubDate><guid>https://glaforge.dev/posts/2017/07/21/apache-groovy-and-google-app-engine-at-javaone/</guid><description>&lt;p>I&amp;rsquo;ll be back at &lt;a href="https://www.oracle.com/javaone/index.html">JavaOne&lt;/a> in San Francisco in October to speak about &lt;a href="http://www.groovy-lang.org/">Apache Groovy&lt;/a> and &lt;a href="https://cloud.google.com/appengine/">Google App Engine&lt;/a>.&lt;/p>
&lt;h2 id="apache-groovy">Apache Groovy&lt;/h2>
&lt;p>I&amp;rsquo;ve been involved with the Apache Groovy project for 14 years now, it&amp;rsquo;s a long time, and it&amp;rsquo;s interesting to see how the language has evolved over time, how it was influenced by other languages, but also how it influenced those other languages itself! Let&amp;rsquo;s see which operators or syntax constructs evolved and moved from one to the other.&lt;/p></description><content:encoded>
<![CDATA[<p>I&rsquo;ll be back at <a href="https://www.oracle.com/javaone/index.html">JavaOne</a> in San Francisco in October to speak about <a href="http://www.groovy-lang.org/">Apache Groovy</a> and <a href="https://cloud.google.com/appengine/">Google App Engine</a>.</p>
<h2 id="apache-groovy">Apache Groovy</h2>
<p>I&rsquo;ve been involved with the Apache Groovy project for 14 years now, it&rsquo;s a long time, and it&rsquo;s interesting to see how the language has evolved over time, how it was influenced by other languages, but also how it influenced those other languages itself! Let&rsquo;s see which operators or syntax constructs evolved and moved from one to the other.</p>
<h2 id="google-app-engine">Google App Engine</h2>
<p>These days, the hype is around containers, containers everywhere! We tend to relegate Platform-as-a-Service solutions to the side, but it&rsquo;s still one of the most convenient way to deploy and scale an application today. After all, Snapchat and others are able to take advantage of a PaaS like App Engine, so why couldn&rsquo;t you too? (and you don&rsquo;t need to scale to their level anyway, but you&rsquo;d still get the convenience of easy development and deployment)</p>
<p>Anyhow, I&rsquo;ve invited my friends from Heroku and Oracle to join me for a panel discussion on the theme of Java PaaS-es. We&rsquo;ll see how Java PaaS-es are relevant today, more than ever.</p>
<h2 id="the-abstracts">The abstracts</h2>
<p>So if you want to lear more about those <a href="https://events.rainfocus.com/catalog/oracle/oow17/catalogjavaone17?search=%22Guillaume%20Laforge%22&amp;showEnrolled=false">two talks</a>, here are their abstracts.</p>
<h3 id="con5034how-languages-influence-each-other-reflections-on-14-years-of-apache-groovy">[CON5034] How Languages Influence Each Other: Reflections on 14 Years of Apache Groovy</h3>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Abstract</span>
      </div>
      <div class="admonition-content">
        <p>Languages have been influencing one another since the dawn of computer programming. There are families of languages: from Algol descendants with begin/end code blocks to those with curly braces such as C. Languages are not invented in a vacuum but are inspired by their predecessors. This session&rsquo;s speaker, who has been working on Apache Groovy for the past 14 years, reflects on the influences that have driven the design of programming languages. In particular, Groovy&rsquo;s base syntax was directly derived from Java&rsquo;s but quickly developed its own flavor, adding closures, type inference, and operators from Ruby. Groovy also inspired other languages: C#, Swift, and JavaScript adopted Groovy&rsquo;s null-safe navigation operator and the famous Elvis operator.</p>
      </div>
    </div><h3 id="con5945java-paas----then-now-and-next">[CON5945] Java PaaS &ndash; Then, Now and Next</h3>

    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Abstract</span>
      </div>
      <div class="admonition-content">
        <p>Java developers want to deploy their apps easily. Fortunately, there are great solutions for them in the form of Platform-as-a-Service for Java. In this discussion panel, we will share the views of Oracle, Heroku and Google engineers about their respective Java PaaS-es, how this space has evolved over the past few years, and what makes a great developer experience for users today. We&rsquo;ll discuss the future of PaaS in light of new technologies like microservices, containerization, and serverless architectures. Finally, we&rsquo;ll open up the space for an interactive discussion with the audience.</p>
      </div>
    </div><p>(with Joe Kutner from Heroku, Shaun Smith from Oracle, Ludovic Champenois from Google, and Frank Greco from NY JavaSIG as moderator)</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Scale an OpenAPI based web API with Cloud Endpoints</title><link>https://glaforge.dev/posts/2017/07/18/scale-an-openapi-based-web-api-with-cloud-endpoints/</link><pubDate>Tue, 18 Jul 2017 23:49:14 +0100</pubDate><guid>https://glaforge.dev/posts/2017/07/18/scale-an-openapi-based-web-api-with-cloud-endpoints/</guid><description>&lt;p>InfoQ recently released a &lt;a href="https://www.infoq.com/presentations/web-api-scale-google-cloud">video&lt;/a> from the &lt;a href="http://paris.apidays.io/">APIDays&lt;/a> conference that took place in Paris last year. I talked about scaling an &lt;a href="https://www.openapis.org/">Open API&lt;/a> based web API using &lt;a href="https://cloud.google.com/endpoints/">Cloud Endpoints&lt;/a>, on the Google Cloud platform.&lt;/p>
&lt;p>I spoke about the topic a few times, as web APIs is a topic I enjoy, at Nordic APIs, at APIDays, or Devoxx. But it&amp;rsquo;s great to see the video online. So let me share the slide deck along with the &lt;a href="https://www.infoq.com/presentations/web-api-scale-google-cloud">video&lt;/a>:&lt;/p></description><content:encoded>
<![CDATA[<p>InfoQ recently released a <a href="https://www.infoq.com/presentations/web-api-scale-google-cloud">video</a> from the <a href="http://paris.apidays.io/">APIDays</a> conference that took place in Paris last year. I talked about scaling an <a href="https://www.openapis.org/">Open API</a> based web API using <a href="https://cloud.google.com/endpoints/">Cloud Endpoints</a>, on the Google Cloud platform.</p>
<p>I spoke about the topic a few times, as web APIs is a topic I enjoy, at Nordic APIs, at APIDays, or Devoxx. But it&rsquo;s great to see the video online. So let me share the slide deck along with the <a href="https://www.infoq.com/presentations/web-api-scale-google-cloud">video</a>:</p>
<script async class="speakerdeck-embed" data-id="490a2aa1c3c142d0ac2c40ea60af2cf5" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>In a nutshell, the API contract is the source of truth. Whether you&rsquo;re the one implementing the API backend, or you&rsquo;re the consumer calling the API, there&rsquo;s this central contract that each party can rely on, to be certain how the API should be looking like, what kind of endpoint to expect, what payloads will be exchanged, or which status codes are used.</p>
<p>With a central contract, team communication and collaboration is facilitated: I&rsquo;ve seen customers where a central architecture team would define a contract, that was implemented by a third-party (an outsourcing consulting company), and the API was consumed by different teams, both internally and externally. The central contract was here to facilitate the work between those teams, to ensure the contract would be fulfilled.</p>
<p>In addition, having such a computer-friendly contract is really useful for tooling. Out of the contract, you can generate various useful artifacts, such as:</p>
<ul>
<li><strong>static &amp; live mocks</strong> &mdash; that consumers can use when the API is not finalized, </li>
<li><strong>test stubs</strong> &mdash; for facilitating integration tests, </li>
<li><strong>server skeletons</strong> &mdash; to get started implementing the business logic of the API with a ready-made project template,</li>
<li><strong>client SDKs</strong> &mdash; offering kits consumers can use, using various languages, to call your API more easily,</li>
<li><strong>sandbox &amp; live playground</strong> &mdash; a visual environment for testing and calling the API, for developers to discover how the API actually works,</li>
<li><strong>an API portal with provisioning</strong> &mdash; a website offering the API reference documentation and allowing developers to get credentials to get access to the API,</li>
<li><strong>static documentation</strong> &mdash; perhaps with just the API reference documentation, or a bundle of useful associated user guide, etc.</li>
</ul>
<p>However, be careful with artifact generation. As soon as you start making some customizations to what&rsquo;s been generated by tools, you might run the risk of overwriting those changes the next time you re-generate those artifacts! So beware, how customization can be done and be integrated with those generated artifacts.</p>
<p>In my presentation and demo, I decided to use Cloud Endpoints to manage my API, and to host the business logic of my API implementation on the Google Cloud Platform. GCP (for short) provides various &ldquo;compute&rdquo; solutions for your projects:</p>
<ul>
<li><a href="https://cloud.google.com/appengine/">Google App Engine</a> (Platform-as-a-Service): you deploy your code, and all the scaling is done transparently for you by the platform,</li>
<li><a href="https://cloud.google.com/container-engine/">Google Container Engine</a> (Container-as-a-Service): it&rsquo;s a Kubernetes-based container orchestrator where you deploy your apps in the form of containers,</li>
<li><a href="https://cloud.google.com/compute/">Google Compute Engine</a> (Infrastructure-as-a-Service): this time, it&rsquo;s full VMs, with even more control on the environment, that you deploy and scale.</li>
</ul>
<p>In my case, I went with a containerized <a href="https://ratpack.io/">Ratpack</a> implementation for my API, implemented using the <a href="http://www.groovy-lang.org/">Apache Groovy</a> programming language (what else? :-). So I deployed my application on Container Engine.</p>
<p>I described my web API via an Open API descriptor, and managed it via Cloud Endpoints. Cloud Endpoints is actually the underlying infrastructure used by Google themselves, to host all the APIs developers can use today (think Google Maps API, etc.) This architecture already serves literally hundreds of billions of requests everyday&hellip; so you can assume it&rsquo;s certainly quite scalable in itself. You can manage APIs described with Open API, regardless of how they were implemented (totally agnostic from the underlying implementation), and it can manage both HTTP-based JSON web APIs, as well as <a href="https://grpc.io/">gRPC</a> based ones.</p>
<p>There are three interesting key aspects to know about Cloud Endpoints, regardless of whether you&rsquo;re using the platform for public / private / mobile / micro-services APIs:</p>
<ul>
<li>Cloud Endpoints takes care of security, to control access to the API, to authenticate consumers (taking advantage of API keys, Firebase auth, Auth0, JSON Web Tokens)</li>
<li>Cloud Endpoints offers logging and monitoring capabilities of key API related metrics</li>
<li>Cloud Endpoints is super snappy and scales nicely as already mentioned (we&rsquo;ll come back to this in a minute)</li>
</ul>
<p>Cloud Endpoints actually offers an <a href="https://github.com/cloudendpoints/esp/">open source &ldquo;sidecar&rdquo; container proxy</a>. Your containerized application will go hand in hand with the <a href="https://cloud.google.com/endpoints/docs/running-esp-localdev">Extensible Service Proxy</a>, and will actually be wrapped by that proxy. All the calls will actually go through that proxy before hitting your own application. Interestingly, there&rsquo;s not one single proxy, but each instance of you app will have its own proxy, thus diminishing the latency between the call to the proxy and the actual code execution in your app (there&rsquo;s no network hop between the two, to a somewhat distant central proxy, as the two containers are together). For the record, this proxy is based on <a href="https://www.nginx.com/">Nginx</a>. And that proxy container can also be run elsewhere, even on your own infrastructure.</p>
<p>In summary, Cloud Endpoints takes care of securing, monitoring and scaling your Web API. Developing, deploying, and managing your API on Google Cloud Platform gives you the choice: in terms of protocol with JSON / HTTP based APIs or gRPC, in terms of implementation technology as you can chose any language or framework you wish that are supported by the various compute options of the platform allow you to go from PaaS, to CaaS, or IaaS. Last but not least, this solution is open: based on open standards like Open API and gRPC, or by implementing its proxy on top of Nginx.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Scale Jenkins with Kubernetes on Google Container Engine</title><link>https://glaforge.dev/talks/2017/07/17/scale-jenkins-with-kubernetes-on-google-container-engine/</link><pubDate>Mon, 17 Jul 2017 10:32:40 +0100</pubDate><guid>https://glaforge.dev/talks/2017/07/17/scale-jenkins-with-kubernetes-on-google-container-engine/</guid><description>&lt;p>Last week, I had the pleasure to speak at the &lt;a href="https://jcd-paris.jfrog.com/">Jenkins Community Day&lt;/a> conference, in Paris, organized by my friends from &lt;a href="https://www.jfrog.com/">JFrog&lt;/a>, provider of awesome tools for software management and distribution. I covered how to scale &lt;a href="https://jenkins.io/">Jenkins&lt;/a> with &lt;a href="https://kubernetes.io/">Kubernetes&lt;/a> on &lt;a href="https://cloud.google.com/container-engine/">Google Container Engine&lt;/a>.&lt;/p>
&lt;p>For the impatient, here are the slides of the presentation I&amp;rsquo;ve given:&lt;/p>
&lt;script async class="speakerdeck-embed" data-id="47af08ec8f0b40e48335ff0390270b28" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js">&lt;/script>
&lt;p>But let&amp;rsquo;s step back a little. In this article, I&amp;rsquo;d like to share with you why you would want to run Jenkins in the cloud, as well as give you some pointers to interesting resources on the topic.&lt;/p></description><content:encoded>
<![CDATA[<p>Last week, I had the pleasure to speak at the <a href="https://jcd-paris.jfrog.com/">Jenkins Community Day</a> conference, in Paris, organized by my friends from <a href="https://www.jfrog.com/">JFrog</a>, provider of awesome tools for software management and distribution. I covered how to scale <a href="https://jenkins.io/">Jenkins</a> with <a href="https://kubernetes.io/">Kubernetes</a> on <a href="https://cloud.google.com/container-engine/">Google Container Engine</a>.</p>
<p>For the impatient, here are the slides of the presentation I&rsquo;ve given:</p>
<script async class="speakerdeck-embed" data-id="47af08ec8f0b40e48335ff0390270b28" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>But let&rsquo;s step back a little. In this article, I&rsquo;d like to share with you why you would want to run Jenkins in the cloud, as well as give you some pointers to interesting resources on the topic.</p>
<h2 id="why-running-jenkins-in-the-cloud">Why running Jenkins in the cloud?</h2>
<p>So why running Jenkins in the cloud? First of all, imagine your small team, working on a single project. You have your own little server, running under a desk somewhere, happily building your application on each commit, a few times a day. So far so good, your build machine running Jenkins isn&rsquo;t too busy, and stays idle most of the day.</p>
<p>Let&rsquo;s do some bottom of the napkin calculations. Let&rsquo;s say you have a team of 3 developers, committing roughly 4 times a day, on one single project, and the build takes roughly 10 minutes to go.</p>
<p>3 developers * 4 commits / day / developer * 10 minutes build time * 1 project = 1 hour 20 minutes</p>
<p>So far so good, your server indeed stays idle most of the day. Usually, at most, your developers will wait just 10 minutes to see the result of their work.</p>
<p>But your team is growing to 10 persons, the team is still as productive, but the project becoming bigger, the build time goes up to 15 minutes:</p>
<p>10 developers * 4 commits / day / developer * 15 minutes build time * 1 project = 10 hours</p>
<p>You&rsquo;re already at 10 hours build time, so your server is busy the whole day, and at times, you might have several build going on at the same time, using several CPU cores in parallel. And instead of building in 15 minutes, sometimes, the build might take longer, or your build might be queued. So in theory, it might be 15 minutes, but in practice, it could be half an hour because of the length of the queue or the longer time to build parallel projects.</p>
<p>Now, the company is successful, and has two projects instead of one (think a backend and a mobile app). Your teams grow further up to 20 developers per project. The developers are a little less productive because of the size of the codebase and project, so they only commit 3 times a day. The build takes more time too, at 20 minutes (in ideal time). Let&rsquo;s do some math again:</p>
<p>20 developers * 3 commits / day / developer * 20 minutes build time * 2 projects = 40 hours</p>
<p>Woh, that&rsquo;s already 40 hours of total build time, if all the builds are run serially. Fortunately, our server is multi-core, but still, there are certainly already many builds that are enqueued, and many of them, perhaps up to 2-3 or perhaps even 4 could be run in parallel. But as we said, the build queue increases further, the real effective time of build is certainly longer than 30 minutes. Perhaps at times, developers won&rsquo;t see the result of their developments before at least an hour, if not more.</p>
<p>One last calculation? With team sizes of 30 developers, decreased productivity of 2 commits, 25 build time, and 3 projects? And you&rsquo;ll get 75 hours total build time. You may start creating a little build farm, with a master and several build agents. But you also increase the burden of server management. Also, if you move towards a full Continuous Delivery or Continuous Deployment approach, you may further increase your build times to go up to deployment, make more but smaller commits, etc. You could think of running builds less often, or even on a nightly basis, to cope with the demand, but then, your company is less agile, and the time-to-market for fixes of new features might increase, and your developers may also become more frustrated because they are developing in the blind, not knowing before the next day if their work was successful or not.</p>
<p>With my calculations, you might think that it makes more sense for big companies, with tons of projects and developers. This is quite true, but when you&rsquo;re a startup, you also want to avoid taking care of local server management, provisioning, etc. You want to be agile, and use only compute resources you need for the time you need them. So even if you&rsquo;re a small startup, a small team, it might still make sense to take advantage of the cloud. You pay only for the actual time taken by your builds as the build agent containers are automatically provisioned and decommissioned. The builds can scale up via Kubernetes, as you need more (or less) CPU time for building everything.</p>
<p>And this is why I was happy to dive into scaling Jenkins in the cloud. For that purpose, I decided to go with building with containers, with <a href="https://kubernetes.io/">Kubernetes</a>, as my app was also containerized as well. <a href="https://cloud.google.com/">Google Cloud</a> offers <a href="https://cloud.google.com/container-engine/">Container Engine</a>, which is basically just Kubernetes in the cloud.</p>
<h2 id="useful-pointers">Useful pointers</h2>
<p>I based my presentation and demo on some great solutions that are published on the Google Cloud documentation portal. Let me give you some pointers:</p>
<ul>
<li><a href="https://cloud.google.com/solutions/jenkins-on-container-engine">Overview of Jenkins on Container Engine</a></li>
<li><a href="https://cloud.google.com/solutions/jenkins-on-container-engine-tutorial">Setting up Jenkins on Container Engine</a></li>
<li><a href="https://cloud.google.com/solutions/configuring-jenkins-container-engine">Configuring Jenkins for Container Engine</a></li>
<li><a href="https://cloud.google.com/solutions/continuous-delivery-jenkins-container-engine">Continuous Deployment to Container Engine using Jenkins</a></li>
<li><a href="https://github.com/GoogleCloudPlatform/continuous-deployment-on-kubernetes">Lab: Build a Continuous Deployment Pipeline with Jenkins and Kubernetes</a></li>
</ul>
<p>The latter one is the tutorial I actually followed for the demo that I presented during the conference. It&rsquo;s a simple Go application, with a frontend and backend. It&rsquo;s continuously build, on each commit (well, every minute to check if there&rsquo;s a new commit), and deployed automatically in different environments: dev, canary, production. The sources of the project are stored in <a href="https://cloud.google.com/source-repositories/">Cloud Source Repository</a> (it can be mirrored from Github, for example). The containers are stored in <a href="https://cloud.google.com/container-registry/">Cloud Container Registry</a>. And both the Jenkins master and agents, as well as the application are running inside Kubernetes clusters in <a href="https://cloud.google.com/container-engine/">Container Engine</a>.</p>
<h2 id="summary-and-perspective">Summary and perspective</h2>
<p>Don&rsquo;t bother with managing servers! Quickly, you&rsquo;ll run out of CPU cycles, and you&rsquo;ll have happier developers with builds that are super snappy!</p>
<p>And for the record, at Google, dev teams are also running Jenkins! There was a presentation (<a href="https://www.youtube.com/watch?v=7ERV9C20GSE">video</a> and <a href="https://www.cloudbees.com/sites/default/files/2016-jenkins-world-jenkins_inside_google.pdf">slides</a> available) given last year by David Hoover at Jenkins World talking about how developers inside Google are running hundreds of build agents to build projects on various platforms.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A year as a Google Cloud Developer Advocate</title><link>https://glaforge.dev/posts/2017/06/13/a-year-as-a-google-cloud-developer-advocate/</link><pubDate>Tue, 13 Jun 2017 11:34:15 +0100</pubDate><guid>https://glaforge.dev/posts/2017/06/13/a-year-as-a-google-cloud-developer-advocate/</guid><description>&lt;p>Time flies! Last week was my first &amp;ldquo;Googleversary&amp;rdquo;: It&amp;rsquo;s already been a year since I joined Google Cloud as a Developer Advocate. What a ride it&amp;rsquo;s been so far!&lt;/p>
&lt;p>I announced my &lt;a href="https://glaforge.dev/posts/2016/06/02/joining-google-as-a-developer-advocate-for-the-google-cloud-platform/">move to Google&lt;/a> in June last year. And since then got the chance to:&lt;/p>
&lt;ul>
&lt;li>talk at more than 20 conferences or meetups&lt;/li>
&lt;li>give 3 keynotes&lt;/li>
&lt;li>write 36 articles&lt;/li>
&lt;li>meet with a dozen customers or so&lt;/li>
&lt;li>addressed literally thousands of developers&lt;/li>
&lt;/ul>
&lt;p>For some conferences, like Devoxx Belgium, I even spoke 5 times! Or for my &lt;a href="https://glaforge.dev/posts/2017/05/16/flying-east-to-singapore/">trip to Singapore&lt;/a>, I had 6 talks or workshops lined up!&lt;/p></description><content:encoded>
<![CDATA[<p>Time flies! Last week was my first &ldquo;Googleversary&rdquo;: It&rsquo;s already been a year since I joined Google Cloud as a Developer Advocate. What a ride it&rsquo;s been so far!</p>
<p>I announced my <a href="https://glaforge.dev/posts/2016/06/02/joining-google-as-a-developer-advocate-for-the-google-cloud-platform/">move to Google</a> in June last year. And since then got the chance to:</p>
<ul>
<li>talk at more than 20 conferences or meetups</li>
<li>give 3 keynotes</li>
<li>write 36 articles</li>
<li>meet with a dozen customers or so</li>
<li>addressed literally thousands of developers</li>
</ul>
<p>For some conferences, like Devoxx Belgium, I even spoke 5 times! Or for my <a href="https://glaforge.dev/posts/2017/05/16/flying-east-to-singapore/">trip to Singapore</a>, I had 6 talks or workshops lined up!</p>
<p>There&rsquo;s a great level of freedom in this job, and you can decide where to put the cursor in terms of travel, or where to put the puck regarding the topics and products to cover. This is really wonderful!</p>
<p>I had the chance to cover things like Google <a href="https://cloud.google.com/appengine/">App Engine</a> (you know I&rsquo;ve been a big fan of it since 2009!), <a href="https://kubernetes.io/">Kubernetes</a> / <a href="https://cloud.google.com/container-engine/">Container Engine</a>, or <a href="https://cloud.google.com/functions/">Cloud Functions</a>, on the compute side of the story, with a bit of <a href="https://cloud.google.com/endpoints/">Cloud Endpoints</a> for the Web API aspect. And on the Big Data / ML side, I talked about the various <a href="https://cloud.google.com/products/machine-learning/">Machine Learning APIs</a> (Vision, Speech recognition, Natural Language processing, Video intelligence), or played with <a href="https://cloud.google.com/bigquery/">BigQuery</a> for analyzing the Github dataset. And lately, my big hit at conferences and meetups, it&rsquo;s been the hot topic of chatbots + serverless, with <a href="https://api.ai/">API.AI</a> and Cloud Functions for building conversational interfaces.</p>
<p>But there are just so many hours in a day, and there&rsquo;s so much more I want to work on and play with! As I often tell friends and developers that I meet here and there: working as a developer advocate for Google Cloud, it&rsquo;s a bit like being a kid entering a toy store where you&rsquo;d be told: &ldquo;hey, here are all the toys we have, feel free to play with everything you want!&rdquo; But it&rsquo;s not only the toys&hellip; you also get the chance to play with other kids (err, sorry, fellow DAs) on all those things, to imagine the next epic game together!</p>
<p>I&rsquo;m looking forward to telling you more about Google Cloud! Let&rsquo;s meet and chat at the next conference or meetup!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Trying out Apache Groovy's new Antlr4 parser with Java 8 support</title><link>https://glaforge.dev/posts/2017/05/29/trying-out-apache-groovy-new-antlr4-parser-with-java-8-support/</link><pubDate>Mon, 29 May 2017 11:37:52 +0100</pubDate><guid>https://glaforge.dev/posts/2017/05/29/trying-out-apache-groovy-new-antlr4-parser-with-java-8-support/</guid><description>&lt;p>&lt;a href="http://www.groovy-lang.org/">Apache Groovy&lt;/a> is coming up with a new parser, that supports the Java 8 syntax elements, as well as some new notation and operators of its own (like !in, !instanceof or ?[] for safe navigation with collections, or with ?= for Elvis assignment). I blogged recently about the fact that you can &lt;a href="https://glaforge.dev/posts/2017/03/24/testing-java-8-snippets-on-the-new-app-engine-java-8-runtime/">try this new flavor online on this forked Groovy Web Console&lt;/a> version, without the need of installing everything. But today I&amp;rsquo;ll tell you how to build it for yourself in order to run it on your machine.&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://www.groovy-lang.org/">Apache Groovy</a> is coming up with a new parser, that supports the Java 8 syntax elements, as well as some new notation and operators of its own (like !in, !instanceof or ?[] for safe navigation with collections, or with ?= for Elvis assignment). I blogged recently about the fact that you can <a href="https://glaforge.dev/posts/2017/03/24/testing-java-8-snippets-on-the-new-app-engine-java-8-runtime/">try this new flavor online on this forked Groovy Web Console</a> version, without the need of installing everything. But today I&rsquo;ll tell you how to build it for yourself in order to run it on your machine.</p>
<p>It&rsquo;s still to be decided which is going to be the version number of the release containing the new &ldquo;parrot&rdquo; parser, but you can already play with this syntax today. As I&rsquo;m the kind of guy living on the edge of Groovy, I always use Groovy from the firehose, using the master branch with the latest and greatest changes. But I always forget about the right parameters or environment variable to use to build Groovy with the new parser, and to activate it&hellip; although it&rsquo;s clearly <a href="https://github.com/apache/groovy/tree/master/subprojects/parser-antlr4#how-to-enable-the-new-parser">explained in the documentation</a>. So as a note to self, to know where to look at, I decided to write it down in this post!</p>
<p>Build Apache Groovy from the master branch with the Antlr4 parser:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>./gradlew -PuseAntlr4<span style="color:#666">=</span><span style="color:#007020">true</span> installGroovy
</span></span></code></pre></div><p>It&rsquo;s going to build an installation of Groovy that you can point <a href="http://sdkman.io/">SDKman</a> at:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ sdk install groovy target/install/ dev
</span></span><span style="display:flex;"><span>$ sdk use groovy dev
</span></span></code></pre></div><p>That way, you can use this &ldquo;dev&rdquo; version of Groovy in your shell. However, you still need to enable the Antlr4 parser, and you can do so with the following exported environment variable:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ <span style="color:#007020">export</span> <span style="color:#bb60d5">JAVA_OPTS</span><span style="color:#666">=</span>-Dgroovy.antlr4<span style="color:#666">=</span><span style="color:#007020">true</span>
</span></span></code></pre></div><p>Then, when running groovyConsole or groovysh, you&rsquo;ll be able to try the new syntax.</p>
<p>To learn more about the new parser, you can have a look at my presentation with some of the novelties provided by the &ldquo;parrot&rdquo; parser here:</p>
<script async class="speakerdeck-embed" data-id="cdbf0cee31fd4eeca3c4add6ad86b3b0" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>And you can read Sergio&rsquo;s translation of Daniel&rsquo;s article on the new features <a href="http://sergiodelamo.es/preview-of-groovy-3/">here</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Flying East to Singapore</title><link>https://glaforge.dev/posts/2017/05/16/flying-east-to-singapore/</link><pubDate>Tue, 16 May 2017 11:40:06 +0100</pubDate><guid>https://glaforge.dev/posts/2017/05/16/flying-east-to-singapore/</guid><description>&lt;p>In two weeks, I&amp;rsquo;ll be flying east, much further east than I&amp;rsquo;ve ever been! I&amp;rsquo;ll visit Singapore! And I&amp;rsquo;ll have a pretty busy week with several events: conference, meetup, user groups, brown bag lunch&amp;hellip; and I&amp;rsquo;ll talk about Groovy, Machine Learning, and chatbots!&lt;/p>
&lt;p>First of all, on Wednesday 31st, I&amp;rsquo;ll participate to the &lt;a href="https://www.meetup.com/fr-FR/singajug/events/240026369/?eventId=240026369">Singapore Java User Group&lt;/a>, where I&amp;rsquo;ll give an update on &lt;a href="http://groovy-lang.org/">Apache Groovy&lt;/a> (the latest improvements, new features, the roadmap).&lt;/p></description><content:encoded>
<![CDATA[<p>In two weeks, I&rsquo;ll be flying east, much further east than I&rsquo;ve ever been! I&rsquo;ll visit Singapore! And I&rsquo;ll have a pretty busy week with several events: conference, meetup, user groups, brown bag lunch&hellip; and I&rsquo;ll talk about Groovy, Machine Learning, and chatbots!</p>
<p>First of all, on Wednesday 31st, I&rsquo;ll participate to the <a href="https://www.meetup.com/fr-FR/singajug/events/240026369/?eventId=240026369">Singapore Java User Group</a>, where I&rsquo;ll give an update on <a href="http://groovy-lang.org/">Apache Groovy</a> (the latest improvements, new features, the roadmap).</p>
<p><figure>
  <a href="#img-79424708b4386e138d3efe736a897c78">
    <img src="/img/sin/sin-jug-groovy.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-79424708b4386e138d3efe736a897c78">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/sin/sin-jug-groovy.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Thursday 1st, I&rsquo;ll visit <a href="https://www.ca-cib.com/our-global-presence/asia-pacific/singapore">Crédit Agricole CIB</a>, for a brown-bag lunch about Machine Learning. They did a fun survey to decide which topics I should cover! And funnily, this is in this office that is maintained and developed a banking project on which I worked more than ten years before!</p>
<p><figure>
  <a href="#img-419c37ec74a7e0ece9c4137d827c4851">
    <img src="/img/sin/sin-ca.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-419c37ec74a7e0ece9c4137d827c4851">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/sin/sin-ca.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Then, it&rsquo;s Voxxed Days Singapore time! I&rsquo;ll talk again about the Machine Learning APIs offered by Google Cloud, as well as a few words about TensorFlow and Cloud Machine Learning Engine.</p>
<p><figure>
  <a href="#img-eb531abb3b892625aff5eeae0d7951cf">
    <img src="/img/sin/sin-voxxed.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-eb531abb3b892625aff5eeae0d7951cf">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/sin/sin-voxxed.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Then, on Saturday 3rd, two events, one organized by the GDG Singapore, which will be a <a href="http://peatix.com/event/263477/view">workshop on building chatbots</a>, using API.AI and Cloud Functions, and covering the Google Home and Assistant too.</p>
<p><figure>
  <a href="#img-de7eb702bcb50b13e029cc3d1f12a4eb">
    <img src="/img/sin/sin-gdg-chatbot.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-de7eb702bcb50b13e029cc3d1f12a4eb">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/sin/sin-gdg-chatbot.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>And the last event will be held in Google&rsquo;s offices, on the theme of <a href="https://events.withgoogle.com/going-beyond-data-science/">data science</a>, where I&rsquo;ll will also give a presentation on chatbots, the Google Assistant with Google Home, with API.AI for the conversational interface, and Cloud Functions for the business logic.</p>
<p><figure>
  <a href="#img-dbae607f6d133dce3f8cc664bb0a517e">
    <img src="/img/sin/sin-data-science.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-dbae607f6d133dce3f8cc664bb0a517e">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/sin/sin-data-science.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>It&rsquo;s gonna be a pretty busy week, and I&rsquo;m looking forward to meeting tons of developers in Singapore, as well as reconnect with some of my friends &amp; former colleagues living in the area!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Machine Learning and Scaling Web Apis</title><link>https://glaforge.dev/talks/2017/05/10/machine-learning-and-scaling-web-apis/</link><pubDate>Wed, 10 May 2017 11:43:01 +0100</pubDate><guid>https://glaforge.dev/talks/2017/05/10/machine-learning-and-scaling-web-apis/</guid><description>&lt;p>The &lt;a href="https://jax.de/">JAX conference&lt;/a>, in Mainz, Germany, is coming to an end. I was there with my colleagues &lt;a href="https://twitter.com/meteatamel">Mete&lt;/a> and &lt;a href="https://twitter.com/hostirosti">Robert&lt;/a>, and had the chance to cover two topics: Machine Learning and Scaling Web APIs. It&amp;rsquo;s a pleasure to come back to this conference where the audience is always very focused, eager to learn, and is engaging in great and interesting conversations.&lt;/p>
&lt;h2 id="machine-intelligence-at-google-scale">Machine Intelligence at Google Scale&lt;/h2>
&lt;p>My &lt;a href="https://jax.de/session/machine-intelligence-at-google-scale-visionspeech-api-tensorflow-and-cloud-machine-learning/">first presentation&lt;/a> was about Machine Learning, and in particular with the Google Cloud APIs, including &lt;a href="https://cloud.google.com/vision/">Vision&lt;/a>, &lt;a href="https://cloud.google.com/speech/">Speech&lt;/a>, &lt;a href="https://cloud.google.com/natural-language/">Natural Language&lt;/a>, &lt;a href="https://cloud.google.com/translate/">Translate&lt;/a>, and &lt;a href="https://cloud.google.com/video-intelligence/">Video Intelligence&lt;/a>. Although I&amp;rsquo;m not an expert in &lt;a href="https://www.tensorflow.org/">TensorFlow&lt;/a> and the &lt;a href="https://cloud.google.com/ml-engine/">Cloud Machine Learning Engine&lt;/a>, I got a chance to say a few words about these. I guess I&amp;rsquo;ll have to play with both at some point to be able to tell even more!&lt;/p></description><content:encoded>
<![CDATA[<p>The <a href="https://jax.de/">JAX conference</a>, in Mainz, Germany, is coming to an end. I was there with my colleagues <a href="https://twitter.com/meteatamel">Mete</a> and <a href="https://twitter.com/hostirosti">Robert</a>, and had the chance to cover two topics: Machine Learning and Scaling Web APIs. It&rsquo;s a pleasure to come back to this conference where the audience is always very focused, eager to learn, and is engaging in great and interesting conversations.</p>
<h2 id="machine-intelligence-at-google-scale">Machine Intelligence at Google Scale</h2>
<p>My <a href="https://jax.de/session/machine-intelligence-at-google-scale-visionspeech-api-tensorflow-and-cloud-machine-learning/">first presentation</a> was about Machine Learning, and in particular with the Google Cloud APIs, including <a href="https://cloud.google.com/vision/">Vision</a>, <a href="https://cloud.google.com/speech/">Speech</a>, <a href="https://cloud.google.com/natural-language/">Natural Language</a>, <a href="https://cloud.google.com/translate/">Translate</a>, and <a href="https://cloud.google.com/video-intelligence/">Video Intelligence</a>. Although I&rsquo;m not an expert in <a href="https://www.tensorflow.org/">TensorFlow</a> and the <a href="https://cloud.google.com/ml-engine/">Cloud Machine Learning Engine</a>, I got a chance to say a few words about these. I guess I&rsquo;ll have to play with both at some point to be able to tell even more!</p>
<blockquote>
<p>The biggest challenge of Deep Learning technology is the scalability. As long as using single GPU server, you have to wait for hours or days to get the result of your work. This doesn&rsquo;t scale for production service, so you need a Distributed Training on the cloud eventually. Google has been building infrastructure for training the large scale neural network on the cloud for years, and now started to share the technology with external developers. In this session, we will introduce new pre-trained ML services such as Cloud Vision API and Speech API that works without any training. Also, we will look how TensorFlow and Cloud Machine Learning will accelerate custom model training for 10x &ndash; 40x with Google&rsquo;s distributed training infrastructure.</p></blockquote>
<script async class="speakerdeck-embed" data-id="b3fe3f4eb13547fc8e81785c54aceafd" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<h2 id="scale-a-swagger-based-web-apiwith-google-cloud-endpoints">Scale a Swagger-based Web API with Google Cloud Endpoints</h2>
<p>My <a href="https://jax.de/session/scale-a-swagger-based-web-api/">second session</a> was about scaling web APIs, defined using the <a href="https://www.openapis.org/">Open API specification</a>, thanks to <a href="https://kubernetes.io/">Kubernetes</a> and <a href="https://cloud.google.com/container-engine/">Google Container Engine</a>, for the scaling part of the story, and managing those APIs thanks to <a href="https://cloud.google.com/endpoints/">Google Cloud Endpoints</a>.</p>
<blockquote>
<p> Web APIs are more often specified with API definition languages like Swagger (now named OpenAPI Spec), as it can help you generate nice interactive documentation, server skeletons, and client SDKs, mocks, and more, making it simpler to get started both producing and consuming an API. In this session, Guillaume will demonstrate how to define a Web API with Swagger/OpenAPI Spec, and scale it using Cloud Endpoints, on the Google Cloud Platform.</p></blockquote>
<script async class="speakerdeck-embed" data-id="490a2aa1c3c142d0ac2c40ea60af2cf5" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A chatbot for the Devoxx conference agenda with API.ai and Cloud Functions</title><link>https://glaforge.dev/talks/2017/04/06/a-chatbot-for-the-devoxx-conference-agenda-with-apiai-and-cloud-functions/</link><pubDate>Thu, 06 Apr 2017 11:51:44 +0100</pubDate><guid>https://glaforge.dev/talks/2017/04/06/a-chatbot-for-the-devoxx-conference-agenda-with-apiai-and-cloud-functions/</guid><description>&lt;p>That&amp;rsquo;s Devoxx France this week, and I&amp;rsquo;ve had the pleasure of delivering today another &lt;a href="https://cfp.devoxx.fr/2017/talk/NSJ-9765/Un_bot_pour_gerer_l'agenda_de_ta_conference">talk on the theme of chatbots, using Cloud Functions for the business logic, API.AI for the bot cleverness&lt;/a>, with a bonus of a demo through Google Home and the Google Assistant platform.&lt;/p>
&lt;p>I&amp;rsquo;ll post the YouTube video recording once it&amp;rsquo;s online, but in the meantime, I wanted to share my slides here:&lt;/p>
&lt;script async class="speakerdeck-embed" data-id="5bbe77c6ff1e4f0da337c7328c75936a" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js">&lt;/script>
&lt;p>And the video in French:&lt;/p></description><content:encoded>
<![CDATA[<p>That&rsquo;s Devoxx France this week, and I&rsquo;ve had the pleasure of delivering today another <a href="https://cfp.devoxx.fr/2017/talk/NSJ-9765/Un_bot_pour_gerer_l'agenda_de_ta_conference">talk on the theme of chatbots, using Cloud Functions for the business logic, API.AI for the bot cleverness</a>, with a bonus of a demo through Google Home and the Google Assistant platform.</p>
<p>I&rsquo;ll post the YouTube video recording once it&rsquo;s online, but in the meantime, I wanted to share my slides here:</p>
<script async class="speakerdeck-embed" data-id="5bbe77c6ff1e4f0da337c7328c75936a" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>And the video in French:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/vNoiTnnlGC0?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Testing Java 8 Snippets on the new App Engine Java 8 runtime</title><link>https://glaforge.dev/posts/2017/03/24/testing-java-8-snippets-on-the-new-app-engine-java-8-runtime/</link><pubDate>Fri, 24 Mar 2017 11:57:41 +0100</pubDate><guid>https://glaforge.dev/posts/2017/03/24/testing-java-8-snippets-on-the-new-app-engine-java-8-runtime/</guid><description>&lt;p>A new Java 8 runtime for &lt;a href="https://cloud.google.com/appengine/docs/standard/">Google App Engine standard&lt;/a> is coming soon, and is currently in alpha testing. You can &lt;a href="https://docs.google.com/a/google.com/forms/d/1MDzykTWp77YzRgFs5R6ONOuKWYnKEhfy5VhSJYbDvmo/viewform?edit_requested=true">request to join&lt;/a> the alpha program, if you want to try it out for yourself. But I wanted to let anyone play with it, easily, to see how well the Java 8 APIs work, but also to try some Java 8 syntax too. So here&amp;rsquo;s a &lt;a href="https://cafe-huitre.appspot.com/">web console&lt;/a> where you can do just that!&lt;/p></description><content:encoded>
<![CDATA[<p>A new Java 8 runtime for <a href="https://cloud.google.com/appengine/docs/standard/">Google App Engine standard</a> is coming soon, and is currently in alpha testing. You can <a href="https://docs.google.com/a/google.com/forms/d/1MDzykTWp77YzRgFs5R6ONOuKWYnKEhfy5VhSJYbDvmo/viewform?edit_requested=true">request to join</a> the alpha program, if you want to try it out for yourself. But I wanted to let anyone play with it, easily, to see how well the Java 8 APIs work, but also to try some Java 8 syntax too. So here&rsquo;s a <a href="https://cafe-huitre.appspot.com/">web console</a> where you can do just that!</p>
<p><a href="https://cafe-huitre.appspot.com/"><figure>
  <a href="#img-10ecd4dcda6274619ff850f74a16dfa6">
    <img src="/img/j8snip/groovy-web-console-java8.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-10ecd4dcda6274619ff850f74a16dfa6">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/j8snip/groovy-web-console-java8.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</a></p>
<p>But to be precise, it&rsquo;s actually my good old <a href="http://groovyconsole.appspot.com/">Groovy Web Console</a>, where people can write, execute and save <a href="http://www.groovy-lang.org/">Apache Groovy</a> snippets. It is a special version, in fact, as it&rsquo;s built on Java 8, uses the invoke dynamic flavor, and&hellip; drum roll&hellip; it&rsquo;s using the upcoming &ldquo;Parrot&rdquo; parser which adds the Java 8 syntax constructs to the Groovy grammar. So not only can you try Java snippets, but it&rsquo;s a great opportunity to try the future Groovy parser that&rsquo;s gonna be released in Apache Groovy 2.5 or 3.0 (still to be decided).</p>
<p>A meetup about Java 8 on Google App Engine standard</p>
<p>Also, for those who live in Paris and the area, we have the chance of having <a href="https://twitter.com/ludoch">Ludovic Champenois</a>, an engineer working on App Engine, that will be in France, and will be speaking at this <a href="https://www.meetup.com/fr-FR/GDG-Cloud-Paris/events/238519360/?rv=ea1&amp;_af=event&amp;_af_eid=238519360&amp;https=on">GDG Cloud meetup hosted by Xebia</a>, which takes places on Tuesday, April 4th, just on the even of Devoxx France!</p>
<p>So if you want to learn more about Java 8 on App Engine, please <a href="https://www.meetup.com/fr-FR/GDG-Cloud-Paris/events/238519360/?rv=ea1&amp;_af=event&amp;_af_eid=238519360&amp;https=on">sign up</a>!</p>
<p>I will also be presenting about <a href="https://madeby.google.com/home/">Google Home</a>, the <a href="https://assistant.google.com/">Google Assistant</a>, <a href="https://api.ai/">API.AI</a>, and Google <a href="https://cloud.google.com/functions/">Cloud Functions</a> to host the logic of your very own bots and agents. It&rsquo;s based on the <a href="https://glaforge.dev/talks/2017/03/13/extending-the-google-assistant-with-actions-on-google/">presentation I gave at Cloud Next</a> 2017 in San Francisco. If you want to learn more about</p>
<p><a href="https://www.meetup.com/fr-FR/GDG-Cloud-Paris/events/238519360/?rv=ea1&amp;_af=event&amp;_af_eid=238519360&amp;https=on"><figure>
  <a href="#img-ffe28be64ed48027d59ef44b43e53ac1">
    <img src="/img/j8snip/gae-home-cf-xebia.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-ffe28be64ed48027d59ef44b43e53ac1">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/j8snip/gae-home-cf-xebia.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</a></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Happy Pi Day! Google Home helps you learn the digits of Pi</title><link>https://glaforge.dev/posts/2017/03/14/happy-pi-day-google-home-helps-you-learn-the-digits-of-pi/</link><pubDate>Tue, 14 Mar 2017 12:10:23 +0100</pubDate><guid>https://glaforge.dev/posts/2017/03/14/happy-pi-day-google-home-helps-you-learn-the-digits-of-pi/</guid><description>&lt;p>You know what? It&amp;rsquo;s Pi Day today! Well, if you follow the American date standard, it&amp;rsquo;s 3.14 today, a nice approximation of Pi. Last year, in a past life, I had &lt;a href="http://restlet.com/company/blog/2016/03/14/win-a-raspberry-pi-3-to-celebrate-pi-day-with-a-pi-api/">played with Pi&lt;/a> already, but this year, my awesome colleagues (&lt;a href="https://twitter.com/saturnism">Ray&lt;/a>, &lt;a href="https://twitter.com/SandeepDinesh">Sandeep&lt;/a>, &lt;a href="https://twitter.com/francesc">Francesc&lt;/a>, &lt;a href="https://twitter.com/IanMLewis">Ian&lt;/a>) have been working on some very cool demos around Pi, with the &amp;ldquo;Pi delivery&amp;rdquo;, at &lt;a href="https://pi.delivery/">https://pi.delivery/&lt;/a>&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-d9aa117e0e1793cbd38fefc95e5fbd5f">
&lt;img src="https://glaforge.dev/img/pi-day/pi-delivery-music.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-d9aa117e0e1793cbd38fefc95e5fbd5f">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/pi-day/pi-delivery-music.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>You can transform the Pi digits in a nice melody, show a D3.js based visualisation of the transitions between digits, you can stream the Pi digits, and more. And you can learn about how it&amp;rsquo;s been &lt;a href="https://pi.delivery/#howcalculating-pi">developed on the Google Cloud Platform&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>You know what? It&rsquo;s Pi Day today! Well, if you follow the American date standard, it&rsquo;s 3.14 today, a nice approximation of Pi. Last year, in a past life, I had <a href="http://restlet.com/company/blog/2016/03/14/win-a-raspberry-pi-3-to-celebrate-pi-day-with-a-pi-api/">played with Pi</a> already, but this year, my awesome colleagues (<a href="https://twitter.com/saturnism">Ray</a>, <a href="https://twitter.com/SandeepDinesh">Sandeep</a>, <a href="https://twitter.com/francesc">Francesc</a>, <a href="https://twitter.com/IanMLewis">Ian</a>) have been working on some very cool demos around Pi, with the &ldquo;Pi delivery&rdquo;, at <a href="https://pi.delivery/">https://pi.delivery/</a></p>
<p><figure>
  <a href="#img-d9aa117e0e1793cbd38fefc95e5fbd5f">
    <img src="/img/pi-day/pi-delivery-music.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-d9aa117e0e1793cbd38fefc95e5fbd5f">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/pi-day/pi-delivery-music.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>You can transform the Pi digits in a nice melody, show a D3.js based visualisation of the transitions between digits, you can stream the Pi digits, and more. And you can learn about how it&rsquo;s been <a href="https://pi.delivery/#howcalculating-pi">developed on the Google Cloud Platform</a>.</p>
<p>Ray pinged me to see if we could also create an assistant you can invoke on Google Home, to ask for digits of Pi, as I recently played with <a href="https://glaforge.dev/talks/2017/03/13/extending-the-google-assistant-with-actions-on-google/">Google Home, API.AI and Cloud Functions</a>! And I played with the idea: created a new Cloud Function that invokes the Pi&rsquo;s Web API, designed an assistant in API.AI, and submitted this assistant to the Google Assistant.</p>
<p>You&rsquo;ll be able to ask your Google Home:</p>

            <link rel="stylesheet" href="/css/vendors/admonitions.d762bab02d2df6f4f18be003fbd11e6175139be403be419f4010274d315eeec0.css" integrity="sha256-12K6sC0t9vTxi&#43;AD&#43;9EeYXUTm&#43;QDvkGfQBAnTTFe7sA=" crossorigin="anonymous">
    <div class="admonition info">
      <div class="admonition-header"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
        <span>Example Interaction</span>
      </div>
      <div class="admonition-content">
        <p>Ok Google, talk to Pi Digit Agent.
What is the 34th digit of Pi?</p>
      </div>
    </div><p>And it will tell you that it&rsquo;s 2.</p>
<p>How did I do that, let&rsquo;s first have a look at the Cloud Function, implemented in JavaScript / Node.js:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;name&#34;</span>: <span style="color:#4070a0">&#34;pi-assistant&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;version&#34;</span>: <span style="color:#4070a0">&#34;0.0.1&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;private&#34;</span>: <span style="color:#007020;font-weight:bold">true</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;scripts&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;start&#34;</span>: <span style="color:#4070a0">&#34;node index.js&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;deploy&#34;</span>: <span style="color:#4070a0">&#34;rm -rf node_modules; gcloud alpha functions deploy digit --project digit-of-pi-2017-assistant  --trigger-http --stage-bucket gs://digit-of-pi-2017-assistant/&#34;</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;description&#34;</span>: <span style="color:#4070a0">&#34;Ask for the n-th digit of Pi!&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;main&#34;</span>: <span style="color:#4070a0">&#34;index.js&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;repository&#34;</span>: <span style="color:#4070a0">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;author&#34;</span>: <span style="color:#4070a0">&#34;Guillaume Laforge&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;dependencies&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;actions-on-google&#34;</span>: <span style="color:#4070a0">&#34;^1.0.7&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;node-fetch&#34;</span>: <span style="color:#4070a0">&#34;^1.6.3&#34;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The key things here are the dependencies: I&rsquo;m using the actions-on-google Node module to interact more easily with API.AI and the Assistant, and I&rsquo;m using node-fetch to interact with the Pi Delivery&rsquo;s REST API.</p>
<p>Let&rsquo;s now have a look at the code of our exported digit function in index.js:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> ApiAiAssistant <span style="color:#666">=</span> require(<span style="color:#4070a0">&#39;actions-on-google&#39;</span>).ApiAiAssistant;
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> fetch <span style="color:#666">=</span> require(<span style="color:#4070a0">&#39;node-fetch&#39;</span>);
</span></span><span style="display:flex;"><span><span style="">​</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">function</span> nthDigit(assistant) {
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">let</span> rank <span style="color:#666">=</span> <span style="color:#007020">parseInt</span>(assistant.getArgument(<span style="color:#4070a0">&#39;rank&#39;</span>).replace(<span style="color:#235388">/,/g</span>, <span style="color:#4070a0">&#39;&#39;</span>));
</span></span><span style="display:flex;"><span>    console.log(<span style="color:#4070a0">`</span><span style="color:#70a0d0">${</span>rank<span style="color:#70a0d0">}</span><span style="color:#4070a0">nth digit`</span>);
</span></span><span style="display:flex;"><span><span style="">​</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// 0 -&gt; 3, 1 -&gt; ., 2 -&gt; 1, 3 -&gt; 4, 4 -&gt; 1, ...
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#60a0b0;font-style:italic">// let&#39;s return 3 for 0th / 1st, and the digit otherwise, 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#60a0b0;font-style:italic">// to follow natural human numbering and the fact the dot is accounted
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="">​</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">let</span> start <span style="color:#666">=</span> rank <span style="color:#666">&lt;</span> <span style="color:#40a070">2</span> <span style="color:#666">?</span> <span style="color:#40a070">0</span> <span style="color:#666">:</span> rank;
</span></span><span style="display:flex;"><span><span style="">​</span>
</span></span><span style="display:flex;"><span>    fetch(<span style="color:#4070a0">`https://api.pi.delivery/v1/pi?start=</span><span style="color:#70a0d0">${</span>start<span style="color:#70a0d0">}</span><span style="color:#4070a0">&amp;numberOfDigits=1`</span>)
</span></span><span style="display:flex;"><span>        .then(response =&gt; response.json())
</span></span><span style="display:flex;"><span>        .then(data =&gt; {
</span></span><span style="display:flex;"><span>            assistant.ask(<span style="color:#4070a0">`Digit </span><span style="color:#70a0d0">${</span>rank<span style="color:#70a0d0">}</span><span style="color:#4070a0"> of Pi is </span><span style="color:#70a0d0">${</span>data.content<span style="color:#70a0d0">}</span><span style="color:#4070a0">. Do you want to know a different digit of Pi? Or say cancel to exit.`</span>);
</span></span><span style="display:flex;"><span>        }).<span style="color:#007020;font-weight:bold">catch</span>(err =&gt; {
</span></span><span style="display:flex;"><span>            console.log(err);
</span></span><span style="display:flex;"><span>            assistant.ask(<span style="color:#4070a0">&#39;The ways of Pi are mysterious... Try again, or with another digit? Or say cancel to exit.&#39;</span>);
</span></span><span style="display:flex;"><span>        });
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="">​</span>
</span></span><span style="display:flex;"><span>exports.digit <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">function</span> (request, response) {
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">let</span> assistant <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> ApiAiAssistant({request, response});
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">let</span> actionMap <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Map();
</span></span><span style="display:flex;"><span>    actionMap.set(<span style="color:#4070a0">&#39;nth-digit-intent&#39;</span>, nthDigit);
</span></span><span style="display:flex;"><span>    assistant.handleRequest(actionMap);
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>It&rsquo;s pretty straightforward, we export a digit function, that creates an API.AI assistant, to which we feed an action map pointing at our main intent, for asking for digits. I extract the parameter (ie. the rank of the digit I&rsquo;m interested in), I call the REST API with a fetch() call, and then I return the result with the assistant.ask() call.</p>
<p>In a nutshell, on API.AI&rsquo;s side, my welcome intent greets you, telling you how to use the assistant:</p>
<p><figure>
  <a href="#img-947065394737cd741cbf7a60ecdc40ae">
    <img src="/img/pi-day/pi-welcome-intent.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-947065394737cd741cbf7a60ecdc40ae">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/pi-day/pi-welcome-intent.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>And then the main intent, whose webhook points at my Cloud Function, does the heavy lifting:</p>
<p><figure>
  <a href="#img-e7f613cfa16f7622a2820943f5ec5181">
    <img src="/img/pi-day/pi-ask-digit-intent.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-e7f613cfa16f7622a2820943f5ec5181">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/pi-day/pi-ask-digit-intent.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>You can try it in the emulator:</p>
<p><figure>
  <a href="#img-d964f245abee27a4d6b7cbdb7adfa33e">
    <img src="/img/pi-day/pi-simulator.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-d964f245abee27a4d6b7cbdb7adfa33e">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/pi-day/pi-simulator.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>After that, once the webhook is properly configured, I published my action, through the integrations pane, and the cloud API console. I&rsquo;ll skip the details here, but you can read more on <a href="https://developers.google.com/actions/distribute/">how to distribution your actions</a>.</p>
<p>So again, Happy Pi Day! And hopefully, if you have a Google Home device and when my assistant is officially published, you&rsquo;ll be able to learn more about the digits of Pi!</p>
<p>And let&rsquo;s finish with a video of the assistant running live on my Google Home!</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/JrqdhpSmNRc?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Extending the Google Assistant with Actions on Google</title><link>https://glaforge.dev/talks/2017/03/13/extending-the-google-assistant-with-actions-on-google/</link><pubDate>Mon, 13 Mar 2017 15:21:06 +0100</pubDate><guid>https://glaforge.dev/talks/2017/03/13/extending-the-google-assistant-with-actions-on-google/</guid><description>&lt;p>Last week, in San Francisco, took place the &lt;a href="https://cloudnext.withgoogle.com/">Google Cloud Next 2017&lt;/a> conference, and I had the pleasure to co-present a session on &amp;ldquo;Extending the Google Assistant with Actions on Google&amp;rdquo;, with Brad Abrams, product manager on the assistant technology at Google.&lt;/p>
&lt;blockquote>
&lt;p>The Google Assistant is the conversational user interface that helps you get things done in your world. Actions on Google let you build on this assistance, while your integrations can help you engage users through Google Home on Pixel, Android and many other devices that connect with Google Assistant. In this session, we&amp;rsquo;ll share the latest innovations behind the Google Assistant and how you can leverage those technologies and best practices for Voice User Interface design to build your own custom extensions to Google Assistant.&lt;/p></description><content:encoded>
<![CDATA[<p>Last week, in San Francisco, took place the <a href="https://cloudnext.withgoogle.com/">Google Cloud Next 2017</a> conference, and I had the pleasure to co-present a session on &ldquo;Extending the Google Assistant with Actions on Google&rdquo;, with Brad Abrams, product manager on the assistant technology at Google.</p>
<blockquote>
<p>The Google Assistant is the conversational user interface that helps you get things done in your world. Actions on Google let you build on this assistance, while your integrations can help you engage users through Google Home on Pixel, Android and many other devices that connect with Google Assistant. In this session, we&rsquo;ll share the latest innovations behind the Google Assistant and how you can leverage those technologies and best practices for Voice User Interface design to build your own custom extensions to Google Assistant.</p></blockquote>
<p>In this presentation, for our demonstration, we used <a href="https://api.ai/">API.AI</a> and <a href="https://cloud.google.com/functions/">Google Cloud Functions</a> (announced as beta during the keynote) to implement our assistant, whose job was to help attendees learn more about the conference schedule and see which talks they&rsquo;d be interested in attending.</p>
<p>You can watch the video of the talk on YouTube already:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/7e0RGIul8Kk?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>And you can have a closer look at the slides below:</p>
<script async class="speakerdeck-embed" data-id="87cda359a4ba4718b789286c1be27379" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Google Cloud Endpoints in General Availability</title><link>https://glaforge.dev/posts/2017/02/13/google-cloud-endpoints-in-general-availability/</link><pubDate>Mon, 13 Feb 2017 15:30:57 +0100</pubDate><guid>https://glaforge.dev/posts/2017/02/13/google-cloud-endpoints-in-general-availability/</guid><description>&lt;p>Today was &lt;a href="http://cloudplatform.googleblog.com/2017/02/Google-Cloud-Endpoints-now-GA-a-fast-scalable-API-gateway.html">announced the general availability&lt;/a> of &lt;a href="https://cloud.google.com/endpoints/">Google Cloud Endpoints&lt;/a>!&lt;/p>
&lt;p>Endpoints is the Google Cloud Platform solution for Web API management, which lets you easily protect &amp;amp; secure your API, monitor it, without overhead, and even allows you to implement your API with any language or framework you want.&lt;/p>
&lt;p>I&amp;rsquo;ve spoken about Endpoints a few times already, at &lt;a href="https://glaforge.dev/talks/2016/11/15/binge-streaming-web-apis-with-ratpack-cloud-ednpoints-app-engine-flex-and-streamdata-io/">Devoxx Belgium&lt;/a>, &lt;a href="https://glaforge.dev/talks/2016/10/27/scaling-a-swagger-based-web-api-on-google-cloud-endpoints/">Nordic APIs summit&lt;/a>, and APIDays Paris. And you can see the recording of my Nordic APIs appearance, if you want to learn more about Cloud Endpoints:&lt;/p></description><content:encoded>
<![CDATA[<p>Today was <a href="http://cloudplatform.googleblog.com/2017/02/Google-Cloud-Endpoints-now-GA-a-fast-scalable-API-gateway.html">announced the general availability</a> of <a href="https://cloud.google.com/endpoints/">Google Cloud Endpoints</a>!</p>
<p>Endpoints is the Google Cloud Platform solution for Web API management, which lets you easily protect &amp; secure your API, monitor it, without overhead, and even allows you to implement your API with any language or framework you want.</p>
<p>I&rsquo;ve spoken about Endpoints a few times already, at <a href="https://glaforge.dev/talks/2016/11/15/binge-streaming-web-apis-with-ratpack-cloud-ednpoints-app-engine-flex-and-streamdata-io/">Devoxx Belgium</a>, <a href="https://glaforge.dev/talks/2016/10/27/scaling-a-swagger-based-web-api-on-google-cloud-endpoints/">Nordic APIs summit</a>, and APIDays Paris. And you can see the recording of my Nordic APIs appearance, if you want to learn more about Cloud Endpoints:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/M1VC5jT-mSU?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A tight development loop for developing bots with API.ai, the Google Cloud Functions emulator, Node.js and Ngrok</title><link>https://glaforge.dev/posts/2017/02/05/a-tight-development-loop-for-developing-bots-with-apiai-the-google-cloud-functions-emulator-nodejs-and-ngrok/</link><pubDate>Sun, 05 Feb 2017 15:33:09 +0100</pubDate><guid>https://glaforge.dev/posts/2017/02/05/a-tight-development-loop-for-developing-bots-with-apiai-the-google-cloud-functions-emulator-nodejs-and-ngrok/</guid><description>&lt;p>For &lt;a href="https://cloudnext.withgoogle.com/">Google Cloud Next&lt;/a> and &lt;a href="http://devoxx.fr/">Devoxx France&lt;/a>, I&amp;rsquo;m working on a new talk showing how to build a conference assistant, to whom you&amp;rsquo;ll be able to ask questions like &amp;ldquo;what is the next talk about Java&amp;rdquo;, &amp;ldquo;when is Guillaume Laforge speaking&amp;rdquo;, &amp;ldquo;what is the topic of the ongoing keynote&amp;rdquo;, etc.&lt;/p>
&lt;p>For that purpose, I&amp;rsquo;m developing the assistant using &lt;a href="https://api.ai/">API.AI&lt;/a>. It&amp;rsquo;s a &amp;ldquo;conversational user experience platform&amp;rdquo; recently acquired by Google, which allows you to define various &amp;ldquo;intents&amp;rdquo; which correspond to the kind of questions / sentences that a user can say, and various &amp;ldquo;entities&amp;rdquo; which relate to the concepts dealt with (in my example, I have entities like &amp;ldquo;talk&amp;rdquo; or &amp;ldquo;speaker&amp;rdquo;). API.AI lets you define sentences pretty much in free form, and it derives what must be the various entities in the sentences, and is able to actually understand more sentences that you&amp;rsquo;ve given it. Pretty clever machine learning and natural language process at play. In addition to that, you also have support for several spoken languages (English, French, Italian, Chinese and more), integrations with key messaging platforms like Slack, Facebook Messenger, Twilio, or &lt;a href="https://madeby.google.com/home/">Google Home&lt;/a>. It also offers various SDKs so you can integrate it easily in your website, mobile application, backend code (Java, Android, Node, C#&amp;hellip;)&lt;/p></description><content:encoded>
<![CDATA[<p>For <a href="https://cloudnext.withgoogle.com/">Google Cloud Next</a> and <a href="http://devoxx.fr/">Devoxx France</a>, I&rsquo;m working on a new talk showing how to build a conference assistant, to whom you&rsquo;ll be able to ask questions like &ldquo;what is the next talk about Java&rdquo;, &ldquo;when is Guillaume Laforge speaking&rdquo;, &ldquo;what is the topic of the ongoing keynote&rdquo;, etc.</p>
<p>For that purpose, I&rsquo;m developing the assistant using <a href="https://api.ai/">API.AI</a>. It&rsquo;s a &ldquo;conversational user experience platform&rdquo; recently acquired by Google, which allows you to define various &ldquo;intents&rdquo; which correspond to the kind of questions / sentences that a user can say, and various &ldquo;entities&rdquo; which relate to the concepts dealt with (in my example, I have entities like &ldquo;talk&rdquo; or &ldquo;speaker&rdquo;). API.AI lets you define sentences pretty much in free form, and it derives what must be the various entities in the sentences, and is able to actually understand more sentences that you&rsquo;ve given it. Pretty clever machine learning and natural language process at play. In addition to that, you also have support for several spoken languages (English, French, Italian, Chinese and more), integrations with key messaging platforms like Slack, Facebook Messenger, Twilio, or <a href="https://madeby.google.com/home/">Google Home</a>. It also offers various SDKs so you can integrate it easily in your website, mobile application, backend code (Java, Android, Node, C#&hellip;)</p>
<p>When implementing your assistant, you&rsquo;ll need to implement some business logic. You need to retrieve the list of speakers, the list of talks from a backend or REST API. You also need to translate the search for a talk on a given topic into the proper query to that backend. In order to implement such logic, API.AI offers a Webhook interface. You instruct API.AI to point at your own URL that will take care of dealing with the request, and will reply adequately with the right data. To facilitate the development, you can take advantage of the SDKs I mentioned above, or you can also just parse and produce the right JSON payloads. To implement my logic, I decided to use <a href="https://cloud.google.com/functions/">Google Cloud Functions</a>, Google&rsquo;s recent serverless, function-based offering. Cloud Functions is currently is alpha, and supports JavaScript through Node.js.</p>
<p>For brevity sake, I&rsquo;ll focus on a simple example today. I&rsquo;m going to create a small agent that replies to queries like &ldquo;what time is it in Paris&rdquo; or some other city.<br />
In API.AI, we&rsquo;re going to create an &ldquo;city&rdquo; entity with a few city names:</p>
<p><figure>
  <a href="#img-6939ac0c0cfd57998579439d4a090280">
    <img src="/img/tightloop/wtii-01-entity.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-6939ac0c0cfd57998579439d4a090280">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/tightloop/wtii-01-entity.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Next, we&rsquo;re creating the &ldquo;ask-for-the-time&rdquo; intent, with a sentence like &ldquo;what time it is in Paris?&rdquo;:</p>
<p><figure>
  <a href="#img-3c81141f90352e1ce616315165ecdd90">
    <img src="/img/tightloop/wtii-02-intent.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-3c81141f90352e1ce616315165ecdd90">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/tightloop/wtii-02-intent.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Quick remark, when creating my intent, I didn&rsquo;t use the built-in @sys.geo-city data type, I just created my own city kind, but I was pleasantly surprised that it recognized the city name as a potential @sys.geo-city type. Neat!<br />
With our intent and entity ready, we enable the &ldquo;fulfillment&rdquo;, so that API.AI knows it should call our own business logic for replying to that query:</p>
<p><figure>
  <a href="#img-90d17af69b4adf81d2a7458757dd912c">
    <img src="/img/tightloop/wtii-03-fullfillment.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-90d17af69b4adf81d2a7458757dd912c">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/tightloop/wtii-03-fullfillment.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>And that&rsquo;s in the URL field that we&rsquo;ll be able to point at our business logic developed as a Cloud Function. But first, we&rsquo;ll need to implement our function.</p>
<p>After having created a project in the Google Cloud console (you might need to request being whitelisted, as at the time of this writing the product is still in alpha), I create a new function, that I&rsquo;m simply calling &lsquo;agent&rsquo;. I define the function as being triggered by an HTTP call, and with the source code inline.</p>
<p>For the source of my function, I&rsquo;m using the &ldquo;actions-on-google&rdquo; NPM module, that I&rsquo;m defining in the package.json file:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;name&#34;</span>: <span style="color:#4070a0">&#34;what-time-is-it&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;version&#34;</span>: <span style="color:#4070a0">&#34;0.0.1&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;private&#34;</span>: <span style="color:#007020;font-weight:bold">true</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;scripts&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;start&#34;</span>: <span style="color:#4070a0">&#34;node server.js&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;deploy&#34;</span>: <span style="color:#4070a0">&#34;gcloud alpha functions deploy agent --project what-time-is-it-157614  --trigger-http --stage-bucket gs://what-time-is-it-157614/&#34;</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;description&#34;</span>: <span style="color:#4070a0">&#34;An agent to know the time in various cities around the world.&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;main&#34;</span>: <span style="color:#4070a0">&#34;index.js&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;repository&#34;</span>: <span style="color:#4070a0">&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;author&#34;</span>: <span style="color:#4070a0">&#34;Guillaume Laforge&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;dependencies&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;actions-on-google&#34;</span>: <span style="color:#4070a0">&#34;^1.0.5&#34;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>And the implementation looks like the following:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span> ApiAiAssistant <span style="color:#666">=</span> require(<span style="color:#4070a0">&#39;actions-on-google&#39;</span>).ApiAiAssistant;
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> ASK_TIME_INTENT <span style="color:#666">=</span> <span style="color:#4070a0">&#39;ask-for-the-time&#39;</span>;  
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">const</span> CITY <span style="color:#666">=</span> <span style="color:#4070a0">&#39;city&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">function</span> whatTimeIsIt(assistant) {
</span></span><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">var</span> city <span style="color:#666">=</span> assistant.getArgument(CITY);
</span></span><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">if</span> (city <span style="color:#666">===</span> <span style="color:#4070a0">&#39;Paris&#39;</span>) 
</span></span><span style="display:flex;"><span>    assistant.ask(<span style="color:#4070a0">&#34;It&#39;s noon in Paris.&#34;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">else</span> <span style="color:#007020;font-weight:bold">if</span> (city <span style="color:#666">===</span> <span style="color:#4070a0">&#39;London&#39;</span>) 
</span></span><span style="display:flex;"><span>    assistant.ask(<span style="color:#4070a0">&#34;It&#39;s 11 a.m. in London.&#34;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">else</span> 
</span></span><span style="display:flex;"><span>    assistant.ask(<span style="color:#4070a0">&#34;It&#39;s way to early or way too late in &#34;</span> <span style="color:#666">+</span> city);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>exports.agent <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">function</span>(request, response) {
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">var</span> assistant <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> ApiAiAssistant({request<span style="color:#666">:</span> request, response<span style="color:#666">:</span> response});
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">var</span> actionMap <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Map();
</span></span><span style="display:flex;"><span>    actionMap.set(ASK_TIME_INTENT, whatTimeIsIt);
</span></span><span style="display:flex;"><span>    assistant.handleRequest(actionMap);
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>Once my function is created, after 30 seconds or so, the function is actually deployed and ready to serve its first requests. I update the fulfillment details to point at the URL of my newly created cloud function. Then I can use the API.AI console to make a first call to my agent:</p>
<p><figure>
  <a href="#img-bcb676d523ede915760959dbfa96bb2b">
    <img src="/img/tightloop/wtii-04-testing.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-bcb676d523ede915760959dbfa96bb2b">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/tightloop/wtii-04-testing.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>You can see that my function replied it was noon in Paris. When clicking the &ldquo;SHOW JSON&rdquo; button, you can also see the JSON being exchanged:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;id&#34;</span>: <span style="color:#4070a0">&#34;20ef54be-ee01-4fbe-9e6e-e73305046601&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;timestamp&#34;</span>: <span style="color:#4070a0">&#34;2017-02-03T22:22:08.822Z&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;result&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;source&#34;</span>: <span style="color:#4070a0">&#34;agent&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;resolvedQuery&#34;</span>: <span style="color:#4070a0">&#34;what time is it in paris?&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;action&#34;</span>: <span style="color:#4070a0">&#34;ask-for-the-time&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;actionIncomplete&#34;</span>: <span style="color:#007020;font-weight:bold">false</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;parameters&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;city&#34;</span>: <span style="color:#4070a0">&#34;Paris&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;contexts&#34;</span>: [
</span></span><span style="display:flex;"><span>      {
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;name&#34;</span>: <span style="color:#4070a0">&#34;_actions_on_google_&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;parameters&#34;</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&#34;city&#34;</span>: <span style="color:#4070a0">&#34;Paris&#34;</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&#34;city.original&#34;</span>: <span style="color:#4070a0">&#34;Paris&#34;</span>
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;lifespan&#34;</span>: <span style="color:#40a070">100</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    ],
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;metadata&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;intentId&#34;</span>: <span style="color:#4070a0">&#34;b98aaae0-838a-4d55-9c8d-6adef4a4d798&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;webhookUsed&#34;</span>: <span style="color:#4070a0">&#34;true&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;webhookForSlotFillingUsed&#34;</span>: <span style="color:#4070a0">&#34;true&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;intentName&#34;</span>: <span style="color:#4070a0">&#34;ask-for-the-time&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;fulfillment&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;speech&#34;</span>: <span style="color:#4070a0">&#34;It&#39;s noon in Paris.&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;messages&#34;</span>: [
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#40a070">0</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&#34;speech&#34;</span>: <span style="color:#4070a0">&#34;It&#39;s noon in Paris.&#34;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      ],
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;data&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;google&#34;</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&#34;expect_user_response&#34;</span>: <span style="color:#007020;font-weight:bold">true</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&#34;is_ssml&#34;</span>: <span style="color:#007020;font-weight:bold">false</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&#34;no_input_prompts&#34;</span>: []
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;score&#34;</span>: <span style="color:#40a070">1</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;status&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;code&#34;</span>: <span style="color:#40a070">200</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;errorType&#34;</span>: <span style="color:#4070a0">&#34;success&#34;</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;sessionId&#34;</span>: <span style="color:#4070a0">&#34;4ba74fa2-e462-4992-9587-2439b32aad3d&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>So far so good, it worked. But as you start fleshing out your agent, you&rsquo;re going to continue making tests manually, then update your code and redeploy the function, several times. Although the deployment times of Cloud Function is pretty fast (30 seconds or so), as you make even simple tweaks to your function&rsquo;s source code, adding several times 30 seconds, you will quickly feel like you&rsquo;re wasting a bit of time waiting for those deployments. What if&hellip; you could run your function locally on your machine, let API.AI point at your local machine somehow through its fulfillment configuration, and make changes live to your code, and test the changes right away without needing any redeployment! We can! We are going to do so by using the Cloud Functions emulator, as well as the very nice ngrok tool which allows you to expose your local host to the internet. Let&rsquo;s install the Cloud Functions emulator, as shown in its documentation:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>npm install -g @google-cloud/functions-emulator
</span></span></code></pre></div><p>Earlier, we entered the code of our function (index.js and package.json) directly in the Google Cloud Platform web console, but we will now retrieve them locally, to run them from our own machine. We will also need to install the actions-on-google npm module for our project to run:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>npm install actions-on-google
</span></span></code></pre></div><p>Once the emulator is installed (you&rsquo;ll need at least Node version 6.9), you can define your project ID with something like the following (update to your actual project ID):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>functions config <span style="color:#007020">set</span> projectId what-time-is-it-157614
</span></span></code></pre></div><p>And then we can start the emulator, as a daemon, with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>functions start
</span></span></code></pre></div><p>We deploy the function locally with the command:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>functions deploy agent --trigger-http
</span></span></code></pre></div><p>If the function deployed successfully on your machine, you should see the following:</p>
<p><figure>
  <a href="#img-bcdb57e419a58a4299221ec0eb989871">
    <img src="/img/tightloop/witi-05-function-deployed.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-bcdb57e419a58a4299221ec0eb989871">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/tightloop/witi-05-function-deployed.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Notice that your function is running on localhost at:</p>
<pre tabindex="0"><code>http://localhost:8010/what-time-is-it-157614/us-central1/agent
</code></pre><p>We want this function to be accessible from the web. That&rsquo;s where our ngrok magic bullet will help us. Once you&rsquo;ve signed-up to the service and installed it on your machine, you can run ngrok with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ngrok http <span style="color:#40a070">8010</span>
</span></span></code></pre></div><p>The command will expose your service on the web, and allow you to have a public, accessible https endpoint:</p>
<p><figure>
  <a href="#img-1dc1fc77165bbc32b354f3cb124f1cc1">
    <img src="/img/tightloop/wtii-06-ngrok.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-1dc1fc77165bbc32b354f3cb124f1cc1">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/tightloop/wtii-06-ngrok.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>In the API.AI interface, you must update the fulfillment webhook endpoint to point to that https URL: <a href="https://acc0889e.ngrok.io/">https://acc0889e.ngrok.io</a>. But you must also append the path shown when running on localhost: what-time-is-it-157614/us-central1/agent, so the full path to indicate in the fulfillment URL will be: <a href="https://acc0889e.ngrok.io/what-time-is-it-157614/us-central1/agent">https://acc0889e.ngrok.io/what-time-is-it-157614/us-central1/agent</a></p>
<p><figure>
  <a href="#img-8c04ad65986783ab4258b8bbcd87e8a3">
    <img src="/img/tightloop/wtii-07-new-fulfillment.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-8c04ad65986783ab4258b8bbcd87e8a3">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/tightloop/wtii-07-new-fulfillment.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Then I use the API.AI console to send another test request, for instance what is the time in San Francisco. And it&rsquo;s calling my local function:</p>
<p><figure>
  <a href="#img-89f7e1b6555c330ecc4150d7c6c8412d">
    <img src="/img/tightloop/wtii-08-typo.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-89f7e1b6555c330ecc4150d7c6c8412d">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/tightloop/wtii-08-typo.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>And in the ngrok local console, you can indeed see that it&rsquo;s my local function that has been called in the emulator:</p>
<p><figure>
  <a href="#img-027e4e592815f9526cb817af3dde0776">
    <img src="/img/tightloop/wtii-09-ngrok.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-027e4e592815f9526cb817af3dde0776">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/tightloop/wtii-09-ngrok.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Nice, it worked! We used the Cloud Functions emulator, in combination with ngrok, to route fulfillment request to our local machine. However, the astute reader might have noticed that my bot&rsquo;s answer contained a typo, I wrote &ldquo;to early&rdquo;, instead of &ldquo;too early&rdquo;. Damn! I&rsquo;ll need to fix that locally, in a tight feedback loop, rather than having to redeploy all the time my function. How do I go about it? I just open my IDE or text editor, fix the typo, and here you go, nothing to redeploy locally or anything, the change is already applied and live. If I make a call in the API.AI console, the typo is fixed:</p>
<p><figure>
  <a href="#img-955ec21997e7753f6350f886407dbf7b">
    <img src="/img/tightloop/wtii-10-typo-fixed.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-955ec21997e7753f6350f886407dbf7b">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/tightloop/wtii-10-typo-fixed.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Thanks to the <a href="https://github.com/GoogleCloudPlatform/cloud-functions-emulator">Cloud Functions emulator</a> and <a href="https://ngrok.com/">ngrok</a>, I can develop locally on my machine, with a tight develop / test loop, without having to deploy my functions all the time. The changes are taken into account live: no need to restart the emulator, or deploy the function locally. Once I&rsquo;m happy with the result, I can deploy for real. Then, I&rsquo;ll have to remember to change the webhook fulfillment URL to the real live cloud function.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>My favorite Cloud Next sessions</title><link>https://glaforge.dev/posts/2017/01/20/my-favorite-cloud-next-sessions/</link><pubDate>Fri, 20 Jan 2017 15:38:37 +0100</pubDate><guid>https://glaforge.dev/posts/2017/01/20/my-favorite-cloud-next-sessions/</guid><description>&lt;p>The &lt;a href="https://cloudnext.withgoogle.com/schedule">schedule&lt;/a> for Google &lt;a href="https://cloudnext.withgoogle.com/">Cloud Next&lt;/a> was unveiled this week, and there&amp;rsquo;s lots of interesting sessions to attend. With the many parallel tracks, it&amp;rsquo;s difficult to make a choice, but I wanted to highlight some of the talks I&amp;rsquo;d like to watch!&lt;/p>
&lt;p>The Google Cloud Platform is a pretty rich one, with many options for your compute needs. How do you choose which one is best for your use case? Brian Dorsey covers this in detail in this session:&lt;/p></description><content:encoded>
<![CDATA[<p>The <a href="https://cloudnext.withgoogle.com/schedule">schedule</a> for Google <a href="https://cloudnext.withgoogle.com/">Cloud Next</a> was unveiled this week, and there&rsquo;s lots of interesting sessions to attend. With the many parallel tracks, it&rsquo;s difficult to make a choice, but I wanted to highlight some of the talks I&rsquo;d like to watch!</p>
<p>The Google Cloud Platform is a pretty rich one, with many options for your compute needs. How do you choose which one is best for your use case? Brian Dorsey covers this in detail in this session:</p>
<p><a href="https://cloudnext.withgoogle.com/schedule#target=where-should-i-run-my-code-deciding-between-compute-engine-container-engine-app-engine-and-more-91e716a3-813e-43c9-a513-27d3365a449b">&ldquo;Where should I run my code?&rdquo; Deciding between Compute Engine, Container Engine, App Engine and more</a></p>
<p>To explore a bit further some of the compute options, I&rsquo;d recommend looking at Container Engine with <a href="https://cloudnext.withgoogle.com/schedule#target=abcs-of-google-container-engine-tips-and-best-practices-03ed20df-b6c9-4e0a-911f-b33017f53477">ABCs of Google Container Engine: tips and best practices</a> by Piotr Szczesniak, and <a href="https://cloudnext.withgoogle.com/schedule#target=go-beyond-paas-with-app-engine-flexible-environment-89abaf38-fce4-451c-b0e3-7726013300df">Go beyond PaaS with App Engine Flexible Environment</a> by Justin Beckwith.</p>
<p>The Serverless trend is strong these days, and in this area, I spotted two slots here with Firebase, Cloud Functions: <a href="https://cloudnext.withgoogle.com/schedule#target=live-coding-a-serverless-app-with-firebase-and-google-cloud-platform-d9026f62-4bfe-4087-b2d8-ffacd52222a2">Live coding a serverless app with Firebase and Google Cloud Platform</a> by Mike McDonald, Jen Tong, Frank van Puffelen, and <a href="https://cloudnext.withgoogle.com/schedule#target=serverless-computing-options-with-google-cloud-platform-1beab0c1-740e-4ee5-82f7-2fc020cdb295">Serverless computing options with Google Cloud Platform</a> by Bret McGowen.</p>
<p>I&rsquo;ve blogged before about Cloud Endpoints, as I&rsquo;m interested in the world of <a href="https://glaforge.dev/tags/web-apis/">Web APIs</a>, and there are two talks I&rsquo;d like to attend in this area: <a href="https://cloudnext.withgoogle.com/schedule#target=google-cloud-endpoints-serving-your-api-to-the-world-8eaeb271-f0ea-4638-af94-16c0b2b80bf6">Google Cloud Endpoints: serving your API to the world</a> by Francesc Campoy Flores and <a href="https://cloudnext.withgoogle.com/schedule#target=authorizing-service-to-service-calls-with-google-cloud-endpoints-24f2852c-e586-4fe9-8eb6-0e13e8585800">Authorizing service-to-service calls with Google Cloud Endpoints</a> by Dan Ciruli, Sep Ebrahimzadeh.</p>
<p>And in my misc. category, I&rsquo;d like to highlight this one on the APIs for G Suite: <a href="https://cloudnext.withgoogle.com/schedule#target=developing-new-apps-built-for-your-organization-with-google-docs-slides-sheets-and-sites-apis-bc17ab71-ce12-4057-bcc6-7fe536f67490">Developing new apps built for your organization with Google Docs, Slides, Sheets and Sites APIs</a> by Ritcha Ranjan. A talk on big parallel data processing with <a href="https://cloudnext.withgoogle.com/schedule#target=using-apache-beam-for-parallel-data-processing-a7a06ee0-7b93-4559-82e0-1a8dfd515771">Using Apache Beam for parallel data processing</a> by Frances Perry.</p>
<p>And to finish, I have to mention my own talk, that I&rsquo;ll be presenting with Brad Abrams: <a href="https://cloudnext.withgoogle.com/schedule#target=talking-to-your-users-build-conversational-actions-for-google-assistant-5a780cf1-4cc2-4ce4-824d-4ff7b7c7a14c">Talking to your users: Build conversational actions for Google Assistant</a>. It should be fun!</p>
<p>What talks are you going to attend?</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Deploy a Ratpack App on Google App Engine Flex</title><link>https://glaforge.dev/posts/2017/01/15/deploy-a-ratpack-app-on-google-app-engine-flex/</link><pubDate>Sun, 15 Jan 2017 15:40:27 +0100</pubDate><guid>https://glaforge.dev/posts/2017/01/15/deploy-a-ratpack-app-on-google-app-engine-flex/</guid><description>&lt;p>The purpose of this article is to deploy a &lt;a href="https://ratpack.io/">Ratpack&lt;/a> web application on &lt;a href="https://cloud.google.com/appengine/docs/flexible/">Google App Engine Flex&lt;/a>.&lt;/p>
&lt;p>For my demos at conferences, I often use frameworks like &lt;a href="https://ratpack.io/">Ratpack&lt;/a>, &lt;a href="https://grails.org/">Grails&lt;/a> or &lt;a href="http://gaelyk.appspot.com/">Gaelyk&lt;/a>, which are based on the &lt;a href="http://www.groovy-lang.org/">Apache Groovy&lt;/a> programming language. In a &lt;a href="https://glaforge.dev/talks/2016/11/15/binge-streaming-web-apis-with-ratpack-cloud-ednpoints-app-engine-flex-and-streamdata-io/">previous article&lt;/a>, I already used Ratpack, but on a slightly more complex use case, but this time, I want to share a quick Ratpack hello world, and deploy it on &lt;a href="https://cloud.google.com/appengine/docs/flexible/">Flex&lt;/a>.&lt;/p>
&lt;p>I started with a hello world template generated by &lt;a href="https://github.com/pledbrook/lazybones">Lazybones&lt;/a> (a simple project creation tool that uses packaged project templates), that I had installed with &lt;a href="http://sdkman.io/">SDKman&lt;/a> (a tool for managing parallel versions of multiple Software Development Kits). But you can go ahead with your own Ratpack apps obviously. Feel free to skip the next section if you already have an app.&lt;/p></description><content:encoded>
<![CDATA[<p>The purpose of this article is to deploy a <a href="https://ratpack.io/">Ratpack</a> web application on <a href="https://cloud.google.com/appengine/docs/flexible/">Google App Engine Flex</a>.</p>
<p>For my demos at conferences, I often use frameworks like <a href="https://ratpack.io/">Ratpack</a>, <a href="https://grails.org/">Grails</a> or <a href="http://gaelyk.appspot.com/">Gaelyk</a>, which are based on the <a href="http://www.groovy-lang.org/">Apache Groovy</a> programming language. In a <a href="https://glaforge.dev/talks/2016/11/15/binge-streaming-web-apis-with-ratpack-cloud-ednpoints-app-engine-flex-and-streamdata-io/">previous article</a>, I already used Ratpack, but on a slightly more complex use case, but this time, I want to share a quick Ratpack hello world, and deploy it on <a href="https://cloud.google.com/appengine/docs/flexible/">Flex</a>.</p>
<p>I started with a hello world template generated by <a href="https://github.com/pledbrook/lazybones">Lazybones</a> (a simple project creation tool that uses packaged project templates), that I had installed with <a href="http://sdkman.io/">SDKman</a> (a tool for managing parallel versions of multiple Software Development Kits). But you can go ahead with your own Ratpack apps obviously. Feel free to skip the next section if you already have an app.</p>
<h2 id="create-a-ratpack-project">Create a Ratpack project</h2>
<pre tabindex="0"><code># install SDKman
curl -s &#34;https://get.sdkman.io&#34; | bash
# install lazybones with sdkman
sdk install lazybones
# create your hello world Ratpack app from a template
lazybones create ratpack flex-test-1
</code></pre><p>You can then quickly run your app with:</p>
<pre tabindex="0"><code>cd flex-test-1
./gradlew run
</code></pre><p>And head your browser to http://localhost:5050 to see your app running.</p>
<p>We&rsquo;ll use the distTar task to create a distribution of our app, so build it with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>./gradlew distTar
</span></span></code></pre></div><h2 id="get-ready-for-flex">Get ready for Flex</h2>
<p>To run our app on App Engine Flex, we&rsquo;ll need to do two things: 1) to containerize it as a Docker container, and 2) to create an app.yaml app descriptor. Let&rsquo;s start with Docker. Create a Dockerfile, and adapt the path names appropriately (replace &ldquo;flex-test-1&rdquo; by the name of the directory you created your project in):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dockerfile" data-lang="dockerfile"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#4070a0"> gcr.io/google_appengine/openjdk8</span><span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">VOLUME</span><span style="color:#4070a0"> /tmp</span><span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">ADD</span> build/distributions/flex-test-1.tar /<span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">ENV</span> <span style="color:#bb60d5">JAVA_OPTS</span><span style="color:#666">=</span><span style="color:#4070a0">&#39;-Dratpack.port=8080 -Djava.security.egd=file:/dev/./urandom&#39;</span><span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">ENTRYPOINT</span> [<span style="color:#4070a0">&#34;/flex-test-1/bin/flex-test-1&#34;</span>]<span style="">
</span></span></span></code></pre></div><p>I&rsquo;m using Open JDK 8 for my <a href="https://cloud.google.com/appengine/docs/flexible/custom-runtimes/">custom runtime</a>. I add my tarred project, and specify port 8080 for running (as requested by Flex), and I define the entry point to my generated startup script.</p>
<p>My app.yaml file, for App Engine Flex, is pretty short, and expresses that I&rsquo;m using the Flexible environment:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">runtime</span>:<span style="color:#bbb"> </span>custom<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">env</span>:<span style="color:#bbb"> </span>flex<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">threadsafe</span>:<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">true</span><span style="color:#bbb">
</span></span></span></code></pre></div><h2 id="create-and-deploy-your-project-on-google-cloud-platform">Create and deploy your project on Google Cloud Platform</h2>
<p>Create an App Engine project on the <a href="https://console.cloud.google.com/appengine">Google Cloud Platform console</a>. And note the project name. You should also <a href="https://cloud.google.com/sdk/downloads">install the gcloud SDK</a> to be able to deploy your Ratpack app from the command-line. Once done, you&rsquo;ll be able to go through the deployment with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud app deploy
</span></span></code></pre></div><p>After a little while, your Ratpack should be up and running!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>New Features in the Google Cloud Natural Language Api Thanks to Your Feedback</title><link>https://glaforge.dev/posts/2017/01/03/new-features-in-the-google-cloud-natural-language-api-thanks-to-your-feedback/</link><pubDate>Tue, 03 Jan 2017 12:06:50 +0100</pubDate><guid>https://glaforge.dev/posts/2017/01/03/new-features-in-the-google-cloud-natural-language-api-thanks-to-your-feedback/</guid><description>&lt;p>&lt;strong>The GA release of Cloud Natural Language API is easier to use, better at recognizing language nuances and adds additional support for Spanish and Japanese&lt;/strong>&lt;/p>
&lt;p>Earlier in November, we announced &lt;a href="https://cloudplatform.googleblog.com/2016/11/Cloud-Machine-Learning-family-grows-with-new-API-editions-and-pricing.html">general availability for the Cloud Natural Language API&lt;/a> and highlighted the &lt;a href="https://cloud.google.com/blog/big-data/2016/11/the-evolution-of-cloud-natural-language-api">key new improvements&lt;/a>. This launch included many additions to the API like expanded entity recognition, granular sentiment analysis with expanded language support, improved syntax analysis with additional morphologies and more.&lt;/p>
&lt;p>Many of these improvements were the result of feedback from beta users, so thank you for your contributions! But concretely, what do these updates mean? Let&amp;rsquo;s take a closer look.&lt;/p></description><content:encoded>
<![CDATA[<p><strong>The GA release of Cloud Natural Language API is easier to use, better at recognizing language nuances and adds additional support for Spanish and Japanese</strong></p>
<p>Earlier in November, we announced <a href="https://cloudplatform.googleblog.com/2016/11/Cloud-Machine-Learning-family-grows-with-new-API-editions-and-pricing.html">general availability for the Cloud Natural Language API</a> and highlighted the <a href="https://cloud.google.com/blog/big-data/2016/11/the-evolution-of-cloud-natural-language-api">key new improvements</a>. This launch included many additions to the API like expanded entity recognition, granular sentiment analysis with expanded language support, improved syntax analysis with additional morphologies and more.</p>
<p>Many of these improvements were the result of feedback from beta users, so thank you for your contributions! But concretely, what do these updates mean? Let&rsquo;s take a closer look.</p>
<h2 id="granular-sentiment-analysis">Granular sentiment analysis</h2>
<p>When analyzing the <a href="http://glaforge.appspot.com/article/natural-language-api-and-javascript-promises-to-bind-them-all">sentiment of the White House speeches</a>, we were interested in the flow of phrases and how sentiment evolves throughout the text. We were looking at each sentence one at a time, making an API call for every sentence! This is obviously not the most efficient way to analyze a long document.</p>
<p>With this new version of the Cloud Natural Language API, you still have the overall sentiment of the article but you also get individual, per-sentence sentiment with a score (ranging between -1 and +1, from negative to positive) and a magnitude. In cases where a sentence or document&rsquo;s score is close to zero, it&rsquo;s helpful to look at magnitude to distinguish between neutral or mixed text. Text with both positive and negative sentiment will have a higher magnitude, whereas text with neutral sentiment will have a magnitude closer to zero. Unlike score, magnitude is not normalized and shows the total amount of sentiment in the text.</p>
<p>Let&rsquo;s look at a few sentences from Mrs. Obama&rsquo;s <a href="https://www.whitehouse.gov/the-press-office/2016/11/15/remarks-first-lady-national-arts-and-humanities-youth-program-awards">address</a> at the National Arts and Humanities Youth Program Awards:</p>
<h3 id="this-kind-of-work-is-hard-too-often-its-thankless-but-you-all-do-it-because-you-see-firsthand-the-transformative-impact-that-the-arts-can-have-on-our-young-people-and-were-grateful-to-you-all-for-doing-this-kind-of-work">This kind of work is hard. Too often it&rsquo;s thankless. But you all do it because you see firsthand the transformative impact that the arts can have on our young people. And we&rsquo;re grateful to you all for doing this kind of work.</h3>
<p>In addition to an overall score of 0.3 and magnitude of 1.7, we also get the sentiment for each sentence:</p>
<p><figure>
  <a href="#img-312e8ced159fabec0fda278de672029b">
    <img src="/img/nlp/new-features-NL-API-16utb.max-1000x1000.png"
      alt="/img/nlp/new-features-NL-API-16utb.max-1000x1000.png"
       />
  </a>
  <figcaption>/img/nlp/new-features-NL-API-16utb.max-1000x1000.png</figcaption>
</figure>
<div class="lightbox" id="img-312e8ced159fabec0fda278de672029b">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/nlp/new-features-NL-API-16utb.max-1000x1000.png"
    alt="/img/nlp/new-features-NL-API-16utb.max-1000x1000.png"
     />
  <div class="lightbox-caption">/img/nlp/new-features-NL-API-16utb.max-1000x1000.png</div>
</div>
</p>
<p>Notice how the first sentences establish a somewhat difficult context with neutral or slightly negative sentiment. The following sentences are much more positive, and with stronger magnitude. Sentiment analysis helps us understand how sentiment evolves throughout our document.</p>
<p>With fine-grained per-sentence, sentiment analysis, there&rsquo;s no need to fire multiple calls to the API &mdash; just one per document. A large document can have widely varying sentiment, and a more detailed analysis of the tone at a granular level provides more insight into the understanding of the text.</p>
<p>For a deep dive on using sentiment analysis on tweets, take a look at <a href="https://cloud.google.com/blog/big-data/2016/09/parsing-our">this blog post</a> about analyzing tweets during the 2016 Rio Olympics. Y Media Labs also published a <a href="https://www.ymedialabs.com/google-sentiment-analysis-api/">post</a> recently about using the Cloud Natural Language API to compare the sentiment of ridesharing apps.</p>
<h2 id="expanded-entity-recognition">Expanded entity recognition</h2>
<p>In a sentence like &ldquo;Adele is an English singer-songwriter,&rdquo; we naturally know that &ldquo;Adele&rdquo; and the &ldquo;English singer-songwriter&rdquo; are the one and only person. But it&rsquo;s not necessarily that trivial for computers. Let&rsquo;s see what the API tells us, when you <a href="https://cloud.google.com/natural-language/">try it online</a>:</p>
<p><figure>
  <a href="#img-5307baec9f328f35b8422f25310b98ac">
    <img src="/img/nlp/new-features-NL-API-2w377.max-800x800.png"
      alt="/img/nlp/new-features-NL-API-2w377.max-800x800.png"
       />
  </a>
  <figcaption>/img/nlp/new-features-NL-API-2w377.max-800x800.png</figcaption>
</figure>
<div class="lightbox" id="img-5307baec9f328f35b8422f25310b98ac">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/nlp/new-features-NL-API-2w377.max-800x800.png"
    alt="/img/nlp/new-features-NL-API-2w377.max-800x800.png"
     />
  <div class="lightbox-caption">/img/nlp/new-features-NL-API-2w377.max-800x800.png</div>
</div>
</p>
<p>Not only does the API point you at the Wikipedia <a href="http://en.wikipedia.org/wiki/Adele">entry</a> about Adele, but it also managed to figure out that both the name and description were about the same person (i.e., entity #1 in red).</p>
<p>If you look at the JSON payload returned:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4070a0">&#34;entities&#34;</span><span style="">:</span> [
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;name&#34;</span>: <span style="color:#4070a0">&#34;Adele&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#4070a0">&#34;PERSON&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;metadata&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&#34;mid&#34;</span>: <span style="color:#4070a0">&#34;/m/02z4b_8&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&#34;wikipedia_url&#34;</span>: <span style="color:#4070a0">&#34;http://en.wikipedia.org/wiki/Adele&#34;</span>
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;salience&#34;</span>: <span style="color:#40a070">0.86802435</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;mentions&#34;</span>: [
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&#34;text&#34;</span>: {
</span></span><span style="display:flex;"><span>                <span style="color:#062873;font-weight:bold">&#34;content&#34;</span>: <span style="color:#4070a0">&#34;Adele&#34;</span>,
</span></span><span style="display:flex;"><span>                <span style="color:#062873;font-weight:bold">&#34;beginOffset&#34;</span>: <span style="color:#40a070">0</span>
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#4070a0">&#34;PROPER&#34;</span>
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&#34;text&#34;</span>: {
</span></span><span style="display:flex;"><span>                <span style="color:#062873;font-weight:bold">&#34;content&#34;</span>: <span style="color:#4070a0">&#34;singer-songwriter&#34;</span>,
</span></span><span style="display:flex;"><span>                <span style="color:#062873;font-weight:bold">&#34;beginOffset&#34;</span>: <span style="color:#40a070">20</span>
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&#34;type&#34;</span>: <span style="color:#4070a0">&#34;COMMON&#34;</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        ]
</span></span><span style="display:flex;"><span>    }<span style="">...</span>
</span></span></code></pre></div><p>Only one entity is returned, but two mentions are given, including the offset where those two mentions appear. The first mention is a proper name, while the second one is just a common name, but still referring to Adele.</p>
<p>Understanding how the various elements of a sentence refer to others is a powerful capability that can now be practiced by software, not just by humans!</p>
<h2 id="improved-syntax-analysis">Improved syntax analysis</h2>
<p>Interested in getting into the nitty-gritty linguistic details of a piece of text? The Cloud Natural Language API&rsquo;s syntax-analysis response now includes much more syntactic data for each word. Let&rsquo;s take this sentence as an example:</p>
<p><figure>
  <a href="#img-c818ba9d4a3bec9b1b4b274a1d1a8149">
    <img src="/img/nlp/new-features-nl-api-5vanr.max-1100x1100.png"
      alt="/img/nlp/new-features-nl-api-5vanr.max-1100x1100.png"
       />
  </a>
  <figcaption>/img/nlp/new-features-nl-api-5vanr.max-1100x1100.png</figcaption>
</figure>
<div class="lightbox" id="img-c818ba9d4a3bec9b1b4b274a1d1a8149">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/nlp/new-features-nl-api-5vanr.max-1100x1100.png"
    alt="/img/nlp/new-features-nl-api-5vanr.max-1100x1100.png"
     />
  <div class="lightbox-caption">/img/nlp/new-features-nl-api-5vanr.max-1100x1100.png</div>
</div>
</p>
<p>In addition to getting the dependency (the arrows in the image above) and part of speech tag for each word, the API returns all sorts of other linguistic info.</p>
<p>Here&rsquo;s the JSON response we get for the word &ldquo;We&rdquo; in the above sentence:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;text&#34;</span>: {
</span></span><span style="display:flex;"><span>       <span style="color:#062873;font-weight:bold">&#34;content&#34;</span>: <span style="color:#4070a0">&#34;We&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;beginOffset&#34;</span>: <span style="color:#40a070">0</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;partOfSpeech&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;tag&#34;</span>: <span style="color:#4070a0">&#34;PRON&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;case&#34;</span>: <span style="color:#4070a0">&#34;NOMINATIVE&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;number&#34;</span>: <span style="color:#4070a0">&#34;PLURAL&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;person&#34;</span>: <span style="color:#4070a0">&#34;FIRST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="">...</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;dependencyEdge&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;headTokenIndex&#34;</span>: <span style="color:#40a070">2</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;label&#34;</span>: <span style="color:#4070a0">&#34;NSUBJ&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;lemma&#34;</span>: <span style="color:#4070a0">&#34;We&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Let&rsquo;s dive deeper into the new values. The token JSON response tells us whether the word is plural or not, indicated by the <code>number</code> value. <code>person</code> tells us that this word is written in the first person. <code>case</code> is a bit more complex, and reveals the function performed by nouns or pronouns in a sentence. &ldquo;We&rdquo; is nominative because it&rsquo;s the subject of the verb &ldquo;are.&rdquo; If the subjects and verbs in the sentence were reversed (&ldquo;They taught us&rdquo;), &ldquo;us&rdquo; would return a value of <code>ACCUSATIVE</code> for case.</p>
<p>The word &ldquo;are&rdquo; in the sentence above returns two more new values:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;mood&#34;</span>: <span style="color:#4070a0">&#34;INDICATIVE&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;tense&#34;</span>: <span style="color:#4070a0">&#34;PRESENT&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>mood</code>, not to be confused with sentiment, refers to the <em>modality</em> of the word or the way in which something is done. The indicative modality in this example shows that this is a statement of fact. Imperative modality, in contrast, describes direct commands. In the sentence &ldquo;Go learn the Natural Language API,&rdquo; the word &ldquo;Go&rdquo; is imperative. This <code>mood</code> value is useful if you&rsquo;re building a bot or automated tool, where you need to understand if something is a statement or command.</p>
<p>The word &ldquo;linguistics&rdquo; in our example also returns a number value of <code>SINGULAR</code>. Even though &ldquo;linguistics&rdquo; could be misinterpreted as a plural form of the word linguistic since it ends in &ldquo;s,&rdquo; the API is able to pick up contextual clues that &ldquo;linguistics&rdquo; here refers to the singular field of linguistics. Finally, the API is able to identify &ldquo;Natural Language API&rdquo; as a proper noun.</p>
<h2 id="improved-support-for-spanish-and-japanese">Improved support for Spanish and Japanese</h2>
<p>The updated API now supports sentiment and additional morphology analysis for Spanish and Japanese. Let&rsquo;s see a couple examples in these languages!</p>
<p>In his poem &ldquo;Canción otoñal&rdquo; from <em>Libro de Poemas</em>, the Spanish poet Federico García Lorca writes: <em>La luz me troncha las alas y el dolor de mi tristeza</em> (which translates to &ldquo;The light shatters my wings and the pain of my sadness.&rdquo;) If you look at the verb &ldquo;troncha&rdquo; (to shatter), the API gives some interesting details:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#4070a0">&#34;partOfSpeech&#34;</span><span style="">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;tag&#34;</span>:    <span style="color:#4070a0">&#34;VERB&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;aspect&#34;</span>: <span style="color:#4070a0">&#34;IMPERFECTIVE&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;mood&#34;</span>:   <span style="color:#4070a0">&#34;INDICATIVE&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;number&#34;</span>: <span style="color:#4070a0">&#34;SINGULAR&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;person&#34;</span>: <span style="color:#4070a0">&#34;THIRD&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;proper&#34;</span>: <span style="color:#4070a0">&#34;NOT_PROPER&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;tense&#34;</span>:  <span style="color:#4070a0">&#34;PRESENT&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;voice&#34;</span>:  <span style="color:#4070a0">&#34;ACTIVE&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="">...</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The verb has an &ldquo;imperfective&rdquo; aspect, an &ldquo;indicative&rdquo; mood, is &ldquo;singular,&rdquo; at the &ldquo;third person&rdquo; and uses an &ldquo;active&rdquo; voice at the &ldquo;present&rdquo; tense. The API knows the Spanish grammar perhaps better than you can remember it!</p>
<p>The Cloud Natural Language API is just as happy to analyze your Japanese content. Let&rsquo;s look at this sentence, which means &ldquo;We are learning more about language through the NL API.&rdquo; What does its structure looks like?</p>
<p><figure>
  <a href="#img-cb8ad8543065ffe9d9fbabadb8b549a5">
    <img src="/img/nlp/new-features-NL-API-34u8u.max-800x800.png"
      alt="/img/nlp/new-features-NL-API-34u8u.max-800x800.png"
       />
  </a>
  <figcaption>/img/nlp/new-features-NL-API-34u8u.max-800x800.png</figcaption>
</figure>
<div class="lightbox" id="img-cb8ad8543065ffe9d9fbabadb8b549a5">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/nlp/new-features-NL-API-34u8u.max-800x800.png"
    alt="/img/nlp/new-features-NL-API-34u8u.max-800x800.png"
     />
  <div class="lightbox-caption">/img/nlp/new-features-NL-API-34u8u.max-800x800.png</div>
</div>
</p>
<h2 id="try-it-in-the-browser">Try it in the browser!</h2>
<p>The new Cloud Natural Language API features provide:</p>
<ul>
<li>Granular sentiment analysis: you get sentiment data for each sentence in your document so you don&rsquo;t need an API call for each sentence. This makes your code more streamlined without requiring costly round-trips to our servers.</li>
<li>Additional morphology details: the subtleties of the language are clearly depicted with more detailed parts of speech tags for each token.</li>
<li>Improved entity recognition: the API now recognizes multiple mentions of the same entity.</li>
<li>Multi-language support: the API added sentiment analysis for Spanish and Japanese along with new morphology details for both languages.</li>
</ul>
<p>Head over to the Google Cloud <a href="https://cloud.google.com/natural-language/#nl_demo_section">Natural Language API page</a> and try these enhancements to the API for yourself in your browser. You&rsquo;ll be able to look-up entities, discover the sentiment of the text and understand the fine-grained details of the structure of the text with hierarchical graphics. You&rsquo;ll notice in the syntax visualization that some words are blue; hover over these words for more morphology details.</p>
<h2 id="next-steps">Next steps</h2>
<p>Want to start using the Cloud Natural Language API in your own apps? Try these next steps:</p>
<ul>
<li>Follow the <a href="https://cloud.google.com/natural-language/docs/getting-started">quickstart guide</a></li>
<li>Let us know what you think in the comments</li>
<li>Use the <a href="http://stackoverflow.com/questions/tagged/google-cloud-nl">google-cloud-nl</a> tag on Stack Overflow</li>
<li>Learn more about <a href="https://cloud.google.com/products/machine-learning/">other Cloud APIs for machine learning</a></li>
<li>Get trained on Cloud ML at a Next &lsquo;17 <a href="https://cloudnext.withgoogle.com/bootcamps/cloud-ml">technical bootcamp</a> on March 6-7 in San Francisco</li>
</ul>
<p>We&rsquo;re excited to see what you build!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A poor-man assistant with speech recognition and natural language processing</title><link>https://glaforge.dev/posts/2016/12/16/a-poor-man-assistant-with-speech-recognition-and-natural-language-processing/</link><pubDate>Fri, 16 Dec 2016 15:43:35 +0100</pubDate><guid>https://glaforge.dev/posts/2016/12/16/a-poor-man-assistant-with-speech-recognition-and-natural-language-processing/</guid><description>&lt;p>All sorts of voice-powered assistants are available today, and chat bots are the new black! In order to illustrate how such tools are made, I decided to create my own little basic conference assistant, using Google&amp;rsquo;s &lt;a href="https://cloud.google.com/speech/">Cloud Speech API&lt;/a> and &lt;a href="https://cloud.google.com/natural-language/">Cloud Natural Language API&lt;/a>. This is a demo I actually created for the Devoxx 2016 keynote, when Stephan Janssen invited me on stage to speak about Machine Learning. And to make this demo more fun, I implemented it with a shell script, some curl calls, plus some other handy command-line tools.&lt;/p></description><content:encoded>
<![CDATA[<p>All sorts of voice-powered assistants are available today, and chat bots are the new black! In order to illustrate how such tools are made, I decided to create my own little basic conference assistant, using Google&rsquo;s <a href="https://cloud.google.com/speech/">Cloud Speech API</a> and <a href="https://cloud.google.com/natural-language/">Cloud Natural Language API</a>. This is a demo I actually created for the Devoxx 2016 keynote, when Stephan Janssen invited me on stage to speak about Machine Learning. And to make this demo more fun, I implemented it with a shell script, some curl calls, plus some other handy command-line tools.</p>
<p>So what is this &ldquo;conference assistant&rdquo; all about? Thanks for asking. The idea is to ask questions to this assistant about topics you&rsquo;d like to see during the conference. For example: &ldquo;Is there a talk about the Google Cloud Vision API?&rdquo;. You send that voice request to the Speech API, which gives you back the transcript of the question. You can then use the Natural Language API to process that text to extract the relevant topic in that question. Then you query the conference schedule to see if there&rsquo;s a talk matching the topic.</p>
<p>Let&rsquo;s see this <a href="https://asciinema.org/a/95040">demo into action</a>, before diving into the details.</p>
<p>So how did I create this little command-line conference assistant? Let&rsquo;s start with a quick diagram showing the whole process and its steps:</p>
<p><figure>
  <a href="#img-fadbf4f963088173f6eeb952406dc93d">
    <img src="/img/misc/assistant-diagram.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-fadbf4f963088173f6eeb952406dc93d">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/assistant-diagram.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<ul>
<li>First, I record the audio using the <a href="http://sox.sourceforge.net/">sox</a> command-line tool.</li>
<li>The audio file is saved locally, and I upload it to Google Cloud Storage (GCS).</li>
<li>I then call the Speech API, pointing it at my recorded audio file in GCS, so that it returns the text it recognized from the audio.</li>
<li>I use the <a href="https://stedolan.github.io/jq/">jq</a> command line tool to extract the words from the returned JSON payload, and only the words I&rsquo;m interested in (basically what appears after the &ldquo;about&rdquo; part of my query, ie. &ldquo;a talk <em>about</em> machine learning&rdquo;)</li>
<li>Lastly, I&rsquo;m calling a <a href="https://cse.google.com/">custom search engine</a> that points at the conference website schedule, to find the relevant talks that match my search query.</li>
</ul>
<p>Let&rsquo;s have a look at the script in more details (this is the simplified script without all the shiny terminal colors and logging output). You should create a project in the Google Cloud Console, and note its project ID, as we&rsquo;ll reuse it for storing our audio file.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007020">#!/bin/bash
</span></span></span><span style="display:flex;"><span><span style="color:#007020"></span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># create an API key to access the Speech and NL APIs</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># https://support.google.com/cloud/answer/6158862?hl=en</span>
</span></span><span style="display:flex;"><span><span style="color:#007020">export</span> <span style="color:#bb60d5">API_KEY</span><span style="color:#666">=</span>YOUR API KEY HERE
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># create a Google Custom Search and retrieve its id</span>
</span></span><span style="display:flex;"><span><span style="color:#007020">export</span> <span style="color:#bb60d5">CS_ID</span><span style="color:#666">=</span>THE ID OF YOUR GOOGLE CUSTOM SEARCH
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># to use sox for recording audio, you can install it with:</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># brew install sox --with-lame --with-flac --with-libvorbis</span>
</span></span><span style="display:flex;"><span>sox  -d -r 16k -c <span style="color:#40a070">1</span> query.flac
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># once the recording is over, hit CTRL-C to stop</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># upload the audio file to Google Cloud Storage with the gsutil command</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># see the documentation for installing it, as well as the gcloud CLI</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># https://cloud.google.com/storage/docs/gsutil_install</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># https://cloud.google.com/sdk/docs/</span>
</span></span><span style="display:flex;"><span>gsutil copy -a public-read query.flac gs://devoxx-ml-demo.appspot.com/query.flac
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># call the Speech API with the template request saved in speech-request.json:</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># {</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">#  &#34;config&#34;: {</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">#      &#34;encoding&#34;:&#34;FLAC&#34;,</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">#      &#34;sample_rate&#34;: 16000,</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">#      &#34;language_code&#34;: &#34;en-US&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">#  },</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">#  &#34;audio&#34;: {</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">#      &#34;uri&#34;:&#34;gs://YOUR-PROJECT-ID-HERE.appspot.com/query.flac&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">#  }</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">#}</span>
</span></span><span style="display:flex;"><span>curl -s -X POST -H <span style="color:#4070a0">&#34;Content-Type: application/json&#34;</span> --data-binary @speech-request.json <span style="color:#4070a0">&#34;https://speech.googleapis.com/v1beta1/speech:syncrecognize?key=</span><span style="color:#70a0d0">${</span><span style="color:#bb60d5">API_KEY</span><span style="color:#70a0d0">}</span><span style="color:#4070a0">&#34;</span> &gt; speech-output.json
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># retrieve the text recognized by the Speech API</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># using the jq to just extract the text part</span>
</span></span><span style="display:flex;"><span>cat speech-output.json | jq -r .results<span style="color:#666">[</span>0<span style="color:#666">]</span>.alternatives<span style="color:#666">[</span>0<span style="color:#666">]</span>.transcript &gt; text.txt
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># prepare a query for the Natural Language API</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># replacing the @TEXT@ place holder with the text we got from Speech API;</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># the JSON query template looks like this:</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># {</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">#  &#34;document&#34;: {</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">#    &#34;type&#34;: &#34;PLAIN_TEXT&#34;,</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">#    &#34;content&#34;: &#34;@TEXT@&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">#  },</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">#  &#34;features&#34;: {</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">#    &#34;extractSyntax&#34;: true,</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">#    &#34;extractEntities&#34;: false,</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">#    &#34;extractDocumentSentiment&#34;: false</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">#  }</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">#}</span>
</span></span><span style="display:flex;"><span>sed <span style="color:#4070a0">&#34;s/@TEXT@/`cat text.txt`/g&#34;</span> nl-request-template.json &gt; nl-request.json
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># call the Natural Language API with our template</span>
</span></span><span style="display:flex;"><span>curl -s -X POST -H <span style="color:#4070a0">&#34;Content-Type: application/json&#34;</span> --data-binary @nl-request.json https://language.googleapis.com/v1beta1/documents:annotateText?key<span style="color:#666">=</span><span style="color:#70a0d0">${</span><span style="color:#bb60d5">API_KEY</span><span style="color:#70a0d0">}</span> &gt; nl-output.json
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># retrieve all the analyzed words from the NL call results</span>
</span></span><span style="display:flex;"><span>cat nl-output.json | jq -r .tokens<span style="color:#666">[]</span>.lemma  &gt; lemmas.txt
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># only keep the words after the &#34;about&#34; word which refer to the topic searched for</span>
</span></span><span style="display:flex;"><span>sed -n <span style="color:#4070a0">&#39;/about/,$p&#39;</span> lemmas.txt | tail -n +2 &gt; keywords.txt
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># join the words together to pass them to the search engine</span>
</span></span><span style="display:flex;"><span>cat keywords.txt | tr <span style="color:#4070a0">&#39;\n&#39;</span> <span style="color:#4070a0">&#39;+&#39;</span> &gt; encoded-keywords.txt
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># call the Google Custom Search engine, with the topic search query</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># and use jq again to filter only the title of the first search result</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"># (the page covering the talk usually comes first)</span>
</span></span><span style="display:flex;"><span>curl -s <span style="color:#4070a0">&#34;https://www.googleapis.com/customsearch/v1?key=</span><span style="color:#bb60d5">$API_KEY</span><span style="color:#4070a0">&amp;cx=</span><span style="color:#bb60d5">$CS_ID</span><span style="color:#4070a0">&amp;q=`cat encoded-keywords.txt`&#34;</span> | jq .items<span style="color:#666">[</span>0<span style="color:#666">]</span>.title
</span></span></code></pre></div><p>And voila, we have our conference assistant on the command-line! We combined the <a href="https://cloud.google.com/speech/">Speech API</a> to recognize the voice and extract the text corresponding to the query audio, we analyze this text with the <a href="https://cloud.google.com/nl">Natural Language API</a>, and we use a few handy command-line tools to do the glue.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Machine intelligence at Google scale, vision / speech APIs, Tensorflow, and Cloud Machine Learning</title><link>https://glaforge.dev/talks/2016/12/04/machine-intelligence-at-google-scale-vision-speech-api-tensorflow-and-cloud-machine-learning/</link><pubDate>Sun, 04 Dec 2016 15:49:55 +0100</pubDate><guid>https://glaforge.dev/talks/2016/12/04/machine-intelligence-at-google-scale-vision-speech-api-tensorflow-and-cloud-machine-learning/</guid><description>&lt;p>With my colleague &lt;a href="https://twitter.com/martin_gorner">Martin Görner&lt;/a>, at the &lt;a href="http://cfp.devoxx.be/2016/talk/HFW-0944/Machine_Intelligence_at_Google_Scale:_Vision%2FSpeech_API,_TensorFlow_and_Cloud_Machine_Learning">Devoxx&lt;/a> conference in Belgium last month, we gave a talk on Machine Learning, on the various APIs provided by Google Cloud, the TensorFlow Machine Learning Open Source project, the Cloud ML service. I didn&amp;rsquo;t get a chance to publish the slides, so it&amp;rsquo;s time I fix that!&lt;/p>
&lt;p>Machine Intelligence at Google Scale: Vision/Speech API, TensorFlow and Cloud Machine Learning&lt;/p>
&lt;p>The biggest challenge of Deep Learning technology is the scalability. As long as using single GPU server, you have to wait for hours or days to get the result of your work. This doesn&amp;rsquo;t scale for production service, so you need a Distributed Training on the cloud eventually. Google has been building infrastructure for training the large scale neural network on the cloud for years, and now started to share the technology with external developers. In this session, we will introduce new pre-trained ML services such as Cloud Vision API and Speech API that works without any training. Also, we will look how TensorFlow and Cloud Machine Learning will accelerate custom model training for 10x - 40x with Google&amp;rsquo;s distributed training infrastructure.&lt;/p></description><content:encoded>
<![CDATA[<p>With my colleague <a href="https://twitter.com/martin_gorner">Martin Görner</a>, at the <a href="http://cfp.devoxx.be/2016/talk/HFW-0944/Machine_Intelligence_at_Google_Scale:_Vision%2FSpeech_API,_TensorFlow_and_Cloud_Machine_Learning">Devoxx</a> conference in Belgium last month, we gave a talk on Machine Learning, on the various APIs provided by Google Cloud, the TensorFlow Machine Learning Open Source project, the Cloud ML service. I didn&rsquo;t get a chance to publish the slides, so it&rsquo;s time I fix that!</p>
<p>Machine Intelligence at Google Scale: Vision/Speech API, TensorFlow and Cloud Machine Learning</p>
<p>The biggest challenge of Deep Learning technology is the scalability. As long as using single GPU server, you have to wait for hours or days to get the result of your work. This doesn&rsquo;t scale for production service, so you need a Distributed Training on the cloud eventually. Google has been building infrastructure for training the large scale neural network on the cloud for years, and now started to share the technology with external developers. In this session, we will introduce new pre-trained ML services such as Cloud Vision API and Speech API that works without any training. Also, we will look how TensorFlow and Cloud Machine Learning will accelerate custom model training for 10x - 40x with Google&rsquo;s distributed training infrastructure.</p>
<p>The video is available on YouTube (in particular, don&rsquo;t miss the cool demos!):</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/zqWt8oI4gEw?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>And you can look at the slides here:</p>
<script async class="speakerdeck-embed" data-id="b0bf5d296e4941ca89fc78c2efb37561" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Analyzing half a million Gradle build files</title><link>https://glaforge.dev/posts/2016/12/03/analyzing-half-a-million-gradle-build-files/</link><pubDate>Sat, 03 Dec 2016 15:52:46 +0100</pubDate><guid>https://glaforge.dev/posts/2016/12/03/analyzing-half-a-million-gradle-build-files/</guid><description>&lt;p>Gradle is becoming the build automation solution of choice among developers, in particular in the Java ecosystem. With the Github archive published as a Google BigQuery dataset, it&amp;rsquo;s possible to analyze those build files, and see if we can learn something interesting about them!&lt;/p>
&lt;p>This week, I was at the G3 Summit conference, and &lt;a href="https://glaforge.dev/talks/2018/03/23/what-do-we-learn-from-millions-of-source-files-in-github/">presented&lt;/a> about this topic: I covered the Apache Groovy language, as per my previous &lt;a href="https://glaforge.dev/posts/2016/07/06/what-can-we-learn-from-million-lines-of-groovy-code-on-github/">article&lt;/a>, but I expanded my queries to also look at &lt;a href="https://grails.org/">Grails&lt;/a> applications, and &lt;a href="https://gradle.org/">Gradle&lt;/a> build files. So let&amp;rsquo;s see what the dataset tells us about Gradle!&lt;/p></description><content:encoded>
<![CDATA[<p>Gradle is becoming the build automation solution of choice among developers, in particular in the Java ecosystem. With the Github archive published as a Google BigQuery dataset, it&rsquo;s possible to analyze those build files, and see if we can learn something interesting about them!</p>
<p>This week, I was at the G3 Summit conference, and <a href="https://glaforge.dev/talks/2018/03/23/what-do-we-learn-from-millions-of-source-files-in-github/">presented</a> about this topic: I covered the Apache Groovy language, as per my previous <a href="https://glaforge.dev/posts/2016/07/06/what-can-we-learn-from-million-lines-of-groovy-code-on-github/">article</a>, but I expanded my queries to also look at <a href="https://grails.org/">Grails</a> applications, and <a href="https://gradle.org/">Gradle</a> build files. So let&rsquo;s see what the dataset tells us about Gradle!</p>
<h2 id="number-of-gradle-build-files-and-repositories">Number of Gradle build files and repositories</h2>
<p>Instead of going through the whole Github dataset, I&rsquo;m going to restrict the dataset by saving only the Gradle build files in my own, smaller, dataset:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span><span style="color:#666">*</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[bigquery<span style="color:#666">-</span><span style="color:#007020;font-weight:bold">public</span><span style="color:#666">-</span><span style="color:#007020;font-weight:bold">data</span>:github_repos.files]<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">WHERE</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">RIGHT</span>(path,<span style="color:#bbb"> </span><span style="color:#40a070">7</span>)<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#39;.gradle&#39;</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>This query returns only the files whose extension is .gradle. I&rsquo;m saving the results in my [github.gradle_build_files] table.</p>
<p>But I also need the content of those files:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span><span style="color:#666">*</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[bigquery<span style="color:#666">-</span><span style="color:#007020;font-weight:bold">public</span><span style="color:#666">-</span><span style="color:#007020;font-weight:bold">data</span>:github_repos.contents]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">WHERE</span><span style="color:#bbb"> </span>id<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">IN</span><span style="color:#bbb"> </span>(<span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>id<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[github.gradle_build_files])<span style="color:#bbb">
</span></span></span></code></pre></div><p>And I will save the content in the table [github.gradle_build_contents].</p>
<p>Let&rsquo;s start with a simple query to count the Gradle build files on Github:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">COUNT</span>(<span style="color:#666">*</span>)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">as</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[github<span style="color:#666">-</span>groovy<span style="color:#666">-</span>files:github.gradle_build_files]<span style="color:#bbb">
</span></span></span></code></pre></div><p>There are 488,311 Gradle build files! Roughly half a million.</p>
<p>This is the number of Gradle files: note that a project can contain several build files, that a repository can contain several projects, but also that the Github dataset only provides data on repositories for which it could detect an Open Source license. So it gives an idea of the reach of Gradle, but doesn&rsquo;t necessarily give you the exact number of Gradle-based projects in the wild! (and obviously can&rsquo;t even account for the projects hosted internally and elsewhere)</p>
<p>Since a repository can contain several build files, let&rsquo;s have a look at the number of repositories containing Gradle build files:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">COUNT</span>(repo_name)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">as</span><span style="color:#bbb"> </span>repos<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>repo_name<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[github<span style="color:#666">-</span>groovy<span style="color:#666">-</span>files:github.gradle_build_files]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">GROUP</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">BY</span><span style="color:#bbb"> </span>repo_name<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb">
</span></span></span></code></pre></div><p>There are 102,803 repositories with Gradle build files.</p>
<p>I was curious to see the distribution of the number of build files across projects. So I used the quantiles function:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>QUANTILES(buildFilesCount,<span style="color:#bbb"> </span><span style="color:#40a070">101</span>)<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>repo_name,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">COUNT</span>(repo_name)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">as</span><span style="color:#bbb"> </span>buildFilesCount<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[github<span style="color:#666">-</span>groovy<span style="color:#666">-</span>files:github.gradle_build_files]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">GROUP</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">BY</span><span style="color:#bbb"> </span>repo_name<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">ORDER</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">BY</span><span style="color:#bbb"> </span>buildFilesCount<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">DESC</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb">
</span></span></span></code></pre></div><p>I used a small increment (one percent), as the data was skewed towards some repositories with a huge amount of Gradle build files: essentially repositories like the Udemy course on Gradle for Android, or an online book about Android development, as they had tons of small build files or variations of build files with incremental changes for explanation purpose.</p>
<ul>
<li>22% of the repositories had only 1 build file</li>
<li>85% of the repositories had up to 5 build files</li>
<li>95% of the repositories had less than 10 build files</li>
</ul>
<p>The repository with the biggest amount of build files had 1333 of them!</p>
<h2 id="gradle-vs-maven">Gradle vs Maven</h2>
<p>You might also be interested in comparing Gradle and Maven, as they are often put against each other in holy build wars. If you look at the number of pom.xml files on Github:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span>(<span style="color:#666">*</span>)<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[bigquery<span style="color:#666">-</span><span style="color:#007020;font-weight:bold">public</span><span style="color:#666">-</span><span style="color:#007020;font-weight:bold">data</span>:github_repos.files]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">WHERE</span><span style="color:#bbb"> </span>path<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">LIKE</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#39;%pom.xml&#39;</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>There are about 1,007,705 pom.xml files vs the 488,311 we counted for Gradle. So roughly twice as many for Maven.</p>
<p>But if you look at the number of repositories with Maven build files:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">COUNT</span>(repo_name)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">as</span><span style="color:#bbb"> </span>repos<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>repo_name<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[bigquery<span style="color:#666">-</span><span style="color:#007020;font-weight:bold">public</span><span style="color:#666">-</span><span style="color:#007020;font-weight:bold">data</span>:github_repos.files]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">WHERE</span><span style="color:#bbb"> </span>path<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">LIKE</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#39;%pom.xml&#39;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">GROUP</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">BY</span><span style="color:#bbb"> </span>repo_name<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb">
</span></span></span></code></pre></div><p>There are 131,037 repositories with Maven pom.xml files, compared to the 102,803 repositories with Gradle build files we counted earlier (about only 27% more). It seems Gradle is catching up with Maven!</p>
<h2 id="gradle-build-file-names">Gradle build file names</h2>
<p>Bigger projects tend to split their build tasks under different build files. I was curious to see which kind of split developers did by looking at the most frequent build file names:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>f,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">COUNT</span>(f)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">as</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">LAST</span>(SPLIT(path,<span style="color:#bbb"> </span><span style="color:#4070a0">&#39;/&#39;</span>))<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">AS</span><span style="color:#bbb"> </span>f<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[github<span style="color:#666">-</span>groovy<span style="color:#666">-</span>files:github.gradle_build_files]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">GROUP</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">BY</span><span style="color:#bbb"> </span>f<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">ORDER</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">BY</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">DESC</span><span style="color:#bbb">
</span></span></span></code></pre></div><p><figure>
  <a href="#img-f39cf0a083d722565e2fa96a07f72a37">
    <img src="/img/bigquery-gradle/gh22-1.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-f39cf0a083d722565e2fa96a07f72a37">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bigquery-gradle/gh22-1.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>

<figure>
  <a href="#img-e4ed5c502192989e7b18b367d70d7425">
    <img src="/img/bigquery-gradle/gh22-2.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-e4ed5c502192989e7b18b367d70d7425">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bigquery-gradle/gh22-2.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Of course, build.gradle comes first. Followed by settings.gradle. Notice the number of build files which are related to making releases, publishing / deploying the artifacts to a repository. There are also a few checking the quality of the code base, using checkstyle for style violations, JaCoCo for code coverage.</p>
<h2 id="gradle-versions">Gradle versions</h2>
<p>Gradle projects often use the Gradle wrapper to help developers use a particular and consistent version of Gradle, without necessiting Gradle to be installed locally. For those developers who decided to commit their Gradle wrapper in Github, we can have a look at the breakdown of Gradle versions currently in the wild:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">version</span>,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">COUNT</span>(<span style="color:#007020;font-weight:bold">version</span>)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">AS</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>REGEXP_EXTRACT(line,<span style="color:#bbb"> </span>r<span style="color:#4070a0">&#39;gradle-(.*)-(?:all|bin).zip&#39;</span>)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">AS</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">version</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>SPLIT(content,<span style="color:#bbb"> </span><span style="color:#4070a0">&#39;\n&#39;</span>)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">AS</span><span style="color:#bbb"> </span>line<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[github<span style="color:#666">-</span>groovy<span style="color:#666">-</span>files:github.gradle_wrapper_properties_files]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">WHERE</span><span style="color:#bbb"> </span>line<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">LIKE</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#39;distributionUrl%&#39;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">GROUP</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">BY</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">version</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">ORDER</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">BY</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">DESC</span><span style="color:#bbb">
</span></span></span></code></pre></div><p><figure>
  <a href="#img-4575c47535e3dc5b4c2adb2bce719e9c">
    <img src="/img/bigquery-gradle/gh20a-1.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-4575c47535e3dc5b4c2adb2bce719e9c">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bigquery-gradle/gh20a-1.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>

<figure>
  <a href="#img-9ceb5e1e5a159f11b5202a51d5679c6a">
    <img src="/img/bigquery-gradle/gh20a-2.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-9ceb5e1e5a159f11b5202a51d5679c6a">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bigquery-gradle/gh20a-2.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>It looks like Gradle 2.4 was a big hit!</p>
<h2 id="gradle-plugins">Gradle plugins</h2>
<p>Gradle projects often take advantage of third-party plugins. You&rsquo;ll see plugins declared with the &ldquo;id&rdquo; syntax or applied with &ldquo;apply plugin&rdquo;. Let&rsquo;s looked at both:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>plugin,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">COUNT</span>(plugin)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">AS</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>REGEXP_EXTRACT(line,<span style="color:#bbb"> </span>r<span style="color:#4070a0">&#39;apply plugin: (?:\&#39;</span><span style="color:#666">|</span><span style="">\</span><span style="color:#4070a0">&#34;)(.*)(?:\&#39;|\&#34;</span>)<span style="color:#4070a0">&#39;) AS plugin
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">  FROM (
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    SELECT SPLIT(content, &#39;</span><span style="">\</span>n<span style="color:#4070a0">&#39;) AS line
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    FROM [github-groovy-files:github.gradle_build_contents]
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">  )
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">)
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">GROUP BY plugin
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">ORDER BY count DESC
</span></span></span></code></pre></div><p><figure>
  <a href="#img-21ca29d20f8b7a97f3c406341e808507">
    <img src="/img/bigquery-gradle/gh23-1.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-21ca29d20f8b7a97f3c406341e808507">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bigquery-gradle/gh23-1.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>

<figure>
  <a href="#img-542307a09687961d4e0dd92c2f022b91">
    <img src="/img/bigquery-gradle/gh23-2.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-542307a09687961d4e0dd92c2f022b91">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bigquery-gradle/gh23-2.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Look at the big number of Android related plugins! Clearly, Android adopting Gradle as build solution gave a big boost to Gradle&rsquo;s adoption!</p>
<p>The plugins declared with &ldquo;id&rdquo; show another story though:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>newplugin,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">COUNT</span>(newplugin)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">AS</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>REGEXP_EXTRACT(line,<span style="color:#bbb"> </span>r<span style="color:#4070a0">&#39;id (?:\&#39;</span><span style="color:#666">|</span><span style="">\</span><span style="color:#4070a0">&#34;)(.*)(?:\&#39;|\&#34;</span>)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">version</span><span style="color:#4070a0">&#39;) AS newplugin
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">  FROM (
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    SELECT SPLIT(content, &#39;</span><span style="">\</span>n<span style="color:#4070a0">&#39;) AS line
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    FROM [github-groovy-files:github.gradle_build_contents]
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">  )
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">)
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">GROUP BY newplugin
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">ORDER BY count DESC
</span></span></span></code></pre></div><p><figure>
  <a href="#img-9a5383096d53d0aff854d327cf4dc795">
    <img src="/img/bigquery-gradle/gh24-1.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-9a5383096d53d0aff854d327cf4dc795">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bigquery-gradle/gh24-1.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>

<figure>
  <a href="#img-702cb36111160f1e996d0252cb7400cc">
    <img src="/img/bigquery-gradle/gh24-2.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-702cb36111160f1e996d0252cb7400cc">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bigquery-gradle/gh24-2.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Here, we see a big usage of the Bintray plugin and the shadow plugin.</p>
<h2 id="build-dependencies">Build dependencies</h2>
<p>Now it&rsquo;s time to look at dependencies. First, the &ldquo;compile&rdquo; dependencies:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>dep,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">COUNT</span>(dep)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">AS</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>REGEXP_EXTRACT(line,<span style="color:#bbb"> </span>r<span style="color:#4070a0">&#39;compile(?: |\()(?:\&#39;</span><span style="color:#666">|</span><span style="">\</span><span style="color:#4070a0">&#34;)(.*):&#39;) AS dep
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">  FROM (
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    SELECT SPLIT(content, &#39;\n&#39;) AS line
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    FROM [github-groovy-files:github.gradle_build_contents]
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">  )
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">)
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">GROUP BY dep
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">ORDER BY count DESC
</span></span></span></code></pre></div><p><figure>
  <a href="#img-965714f76eca56d43bae70f726382ab1">
    <img src="/img/bigquery-gradle/gh25-1.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-965714f76eca56d43bae70f726382ab1">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bigquery-gradle/gh25-1.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>

<figure>
  <a href="#img-3ef86dc9995501b2bb4d6d35928078aa">
    <img src="/img/bigquery-gradle/gh25-2.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-3ef86dc9995501b2bb4d6d35928078aa">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bigquery-gradle/gh25-2.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Again, there&rsquo;s a big usage of Android related dependencies. We also notice Spring Boot, GSON, Guava, SLF4J, Retrofit, Jackson.</p>
<p>For the test dependencies:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>dep,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">COUNT</span>(dep)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">AS</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>REGEXP_EXTRACT(line,<span style="color:#bbb"> </span>r<span style="color:#4070a0">&#39;testCompile(?: |\()(?:\&#39;</span><span style="color:#666">|</span><span style="">\</span><span style="color:#4070a0">&#34;)(.*):&#39;) AS dep
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">  FROM (
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    SELECT SPLIT(content, &#39;\n&#39;) AS line
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    FROM [github-groovy-files:github.gradle_build_contents]
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">  )
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">)
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">GROUP BY dep
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">ORDER BY count DESC
</span></span></span></code></pre></div><p><figure>
  <a href="#img-db751fa2283bdbaae68c496317963962">
    <img src="/img/bigquery-gradle/gh26-1.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-db751fa2283bdbaae68c496317963962">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bigquery-gradle/gh26-1.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>

<figure>
  <a href="#img-d6f08ea97831f2eb98b0b4a06beeee98">
    <img src="/img/bigquery-gradle/gh26-2.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-d6f08ea97831f2eb98b0b4a06beeee98">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bigquery-gradle/gh26-2.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>No big surprise with JUnit coming first. But we have Spock, Mockito&rsquo;s mocking library, AssertJ assertions, Hamcrest matchers.</p>
<h2 id="summary">Summary</h2>
<p>And this wraps up our analysis of Gradle build files, thanks to <a href="https://cloud.google.com/bigquery/">Google BigQuery</a> and the Github dataset. It&rsquo;s interesting to see that Gradle has gained a very significant market share, coming pretty close to the Maven incumbent, and to see lots of Android projects are on Github with Gradle builds.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>My G3 Summit Apache Groovy Keynote</title><link>https://glaforge.dev/talks/2016/11/29/my-g3-summit-apache-groovy-keynote/</link><pubDate>Tue, 29 Nov 2016 16:30:57 +0100</pubDate><guid>https://glaforge.dev/talks/2016/11/29/my-g3-summit-apache-groovy-keynote/</guid><description>&lt;p>This week, I&amp;rsquo;m in Florida for the brand new &lt;a href="https://g3summit.com/conference/fort_lauderdale/2016/11/home">G3 Summit&lt;/a> conference, dedicated to the Apache Groovy ecosystem (Grails, Gradle, and more). I had the chance of giving the keynote, where I gave an overview of the Apache Groovy project&amp;rsquo;s philosophy, history, and where it&amp;rsquo;s heading. In the second part, I&amp;rsquo;m showcasing the new features, new syntax constructs, already there or coming in Groovy 2.4.x, in the future Groovy 2.5, and in Groovy 3.0 with the new parser.&lt;/p></description><content:encoded>
<![CDATA[<p>This week, I&rsquo;m in Florida for the brand new <a href="https://g3summit.com/conference/fort_lauderdale/2016/11/home">G3 Summit</a> conference, dedicated to the Apache Groovy ecosystem (Grails, Gradle, and more). I had the chance of giving the keynote, where I gave an overview of the Apache Groovy project&rsquo;s philosophy, history, and where it&rsquo;s heading. In the second part, I&rsquo;m showcasing the new features, new syntax constructs, already there or coming in Groovy 2.4.x, in the future Groovy 2.5, and in Groovy 3.0 with the new parser.</p>
<script async class="speakerdeck-embed" data-id="ceb97d1f87cc4e2b810914178bb692a9" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Billions of lines of code in a single repository, seriously?</title><link>https://glaforge.dev/talks/2016/11/23/billions-of-lines-of-code-in-a-single-repository-seriously/</link><pubDate>Wed, 23 Nov 2016 16:35:08 +0100</pubDate><guid>https://glaforge.dev/talks/2016/11/23/billions-of-lines-of-code-in-a-single-repository-seriously/</guid><description>&lt;p>When I joined Google last June, I discovered a new world: tons of new acronyms or project code names to learn about, but also a particular environment for your source code. At Google, engineers work on a huge monolithic source code repository comprising of: &lt;/p>
&lt;ul>
&lt;li>1 billion files&lt;/li>
&lt;li>9 million source files&lt;/li>
&lt;li>2 billion lines of code&lt;/li>
&lt;li>35 million commits&lt;/li>
&lt;li>86 terabytes of content&lt;/li>
&lt;li>45 thousands of commits every day.&lt;/li>
&lt;/ul>
&lt;p>Rachel Potvin, who&amp;rsquo;s an engineering manager at Google, wrote &lt;a href="http://cacm.acm.org/magazines/2016/7/204032-why-google-stores-billions-of-lines-of-code-in-a-single-repository/fulltext">an article for ACM&lt;/a> about how Google handles such a huge repository, as well as the tools and practices around that. Wired also covered the topic in their article &amp;ldquo;&lt;a href="https://www.wired.com/2015/09/google-2-billion-lines-codeand-one-place">Google is 2 billion lines of code and it&amp;rsquo;s all in one place&lt;/a>&amp;rdquo;. And Rachel also &lt;a href="https://www.youtube.com/watch?v=W71BTkUbdqE">presented this topic at the @Scale conference&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>When I joined Google last June, I discovered a new world: tons of new acronyms or project code names to learn about, but also a particular environment for your source code. At Google, engineers work on a huge monolithic source code repository comprising of: </p>
<ul>
<li>1 billion files</li>
<li>9 million source files</li>
<li>2 billion lines of code</li>
<li>35 million commits</li>
<li>86 terabytes of content</li>
<li>45 thousands of commits every day.</li>
</ul>
<p>Rachel Potvin, who&rsquo;s an engineering manager at Google, wrote <a href="http://cacm.acm.org/magazines/2016/7/204032-why-google-stores-billions-of-lines-of-code-in-a-single-repository/fulltext">an article for ACM</a> about how Google handles such a huge repository, as well as the tools and practices around that. Wired also covered the topic in their article &ldquo;<a href="https://www.wired.com/2015/09/google-2-billion-lines-codeand-one-place">Google is 2 billion lines of code and it&rsquo;s all in one place</a>&rdquo;. And Rachel also <a href="https://www.youtube.com/watch?v=W71BTkUbdqE">presented this topic at the @Scale conference</a>.</p>
<blockquote>
<p>Google stores all its source code in one single monolithic repository! Imagine 25,000 software developers working simultaneously on 86 TB of data, including two billion lines of code in 9 million unique source files. Each week, there are as many lines of code changed as there are lines in the full Linux kernel repository. How does Google’s source code works at this scale? What are the advantages and drawbacks of such an approach? Come and learn about what it means to work on such a big mammoth repository.</p></blockquote>
<p>You can find the slide deck embedded below:</p>
<script async class="speakerdeck-embed" data-id="d3714d006f694a9f915b8637bb192eea" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>And the talk was also recorded, so you can view the video on Devoxx&rsquo;s YouTube channel here:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/yM0GQw1zgrA?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Becoming Twitter verified</title><link>https://glaforge.dev/posts/2016/11/15/becoming-twitter-verified/</link><pubDate>Tue, 15 Nov 2016 16:50:50 +0100</pubDate><guid>https://glaforge.dev/posts/2016/11/15/becoming-twitter-verified/</guid><description>&lt;p>Probably for vanity sake, or perhaps even out of jealousy seeing friends becoming &amp;ldquo;&lt;a href="https://support.twitter.com/articles/119135">twitter verified&lt;/a>&amp;rdquo;, I was curious to see if, me too, I could get those little ticks beside my name on my &lt;a href="https://twitter.com/glaforge">Twitter profile&lt;/a>.&lt;/p>
&lt;p>Generally speaking, verified accounts are accounts of &amp;ldquo;public interest&amp;rdquo;. It can range from your usual movie stars, to politicians, from well-known artists, to company CEOs, but also persons somehow well known in the twittosphere, including tech luminaries, representative of particular tech communities, etc. So, seeing my tech friends getting the little tick, I thought I should try that out.&lt;/p></description><content:encoded>
<![CDATA[<p>Probably for vanity sake, or perhaps even out of jealousy seeing friends becoming &ldquo;<a href="https://support.twitter.com/articles/119135">twitter verified</a>&rdquo;, I was curious to see if, me too, I could get those little ticks beside my name on my <a href="https://twitter.com/glaforge">Twitter profile</a>.</p>
<p>Generally speaking, verified accounts are accounts of &ldquo;public interest&rdquo;. It can range from your usual movie stars, to politicians, from well-known artists, to company CEOs, but also persons somehow well known in the twittosphere, including tech luminaries, representative of particular tech communities, etc. So, seeing my tech friends getting the little tick, I thought I should try that out.</p>
<p>And on my first try&hellip; I failed&hellip;</p>
<p>I didn&rsquo;t completely failed, as I didn&rsquo;t get the usual message saying I failed verification, but that some tweaks could be made to my profile to add some more information. They asked whether the account name reflected the real person&rsquo;s name (in my case &lsquo;glaforge&rsquo; maps well to my name!), and if my government issued photo ID was legible or not (and it was the case too). So I was puzzled, and didn&rsquo;t really get actionable items to pursue.</p>
<p>For my first attempt, I initially scanned my national ID card, but I guess it&rsquo;s not a very well known format, so on my second try, I instead scanned my passport. Even if I&rsquo;m not feeling safe sending a scan of my ID&hellip; but that&rsquo;s for another story.</p>
<p>I didn&rsquo;t change anything, but two months later, I tried again, and this time it worked!</p>
<p><figure>
  <a href="#img-cccaf7a5be5be147a97f3234976ccd91">
    <img src="/img/misc/glaforge-twitter-verified.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-cccaf7a5be5be147a97f3234976ccd91">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/glaforge-twitter-verified.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>After bragging about being verified on Twitter, I got several DM conversations asking for advice. So I&rsquo;ll just repeat some of the advice given by Twitter themselves, and add my own into the loop.</p>
<p>First, be sure to read the following articles from Twitter:</p>
<ul>
<li><a href="https://support.twitter.com/articles/119135">about verified accounts</a></li>
<li><a href="https://support.twitter.com/articles/20174630">verified accounts</a></li>
<li><a href="https://support.twitter.com/articles/20174631">request to verify an account</a></li>
</ul>
<p>The last one is the most important of the three.</p>
<p>You should really have:</p>
<ul>
<li>a <strong>verified phone number</strong>: It&rsquo;ll be checked with a text message and a code, pretty easy step.</li>
<li>a <strong>confirmed email address</strong>: Same principle, be sure to use a real email address, and a similar verification step takes place.</li>
<li>a <strong>bio</strong>: I think the bio is pretty important. It describes the &ldquo;public&rdquo; person that you are. So if you&rsquo;re a singer, mention that you&rsquo;re a singer and give a link that proves that you are one, for example the website of your band. During the verification, you&rsquo;ll be asked for up to 5 links confirming who you are, so don&rsquo;t hesitate to reuse one of those links in your bio, or related twitter account, etc.</li>
<li>a <strong>header photo</strong>: I don&rsquo;t think the content of the header photo really matters, but verified accounts need to have a header picture. Even if it&rsquo;s just a nice landscape, it should be there, instead of just the colored background.</li>
<li>a <strong>birthday</strong>: I&rsquo;m not sure how important this really is. But if you indicate your birthday, it better be the same date as the one provided on your ID scan obviously!</li>
<li>a <strong>website</strong>: For the website, I used the URL of my blog. And this is also one of the URLs I&rsquo;ve given in the form to request verification. This URL should point at a place that can prove who you are: on my blog, there&rsquo;s a section about me, describing what I&rsquo;m doing in life.</li>
<li><strong>public tweets</strong>: your tweets should be publicly visible, otherwise, no point in requesting being verified!</li>
</ul>
<p>The documentation says it&rsquo;s better if the account name reflects the real name of the person. So it might help. But if you&rsquo;re known with a particular Twitter handle, I don&rsquo;t think you really need to change it for the sake of becoming verified. But it might be harder with a strange Twitter handle than a handle that resembles your name.</p>
<p>When you fill the form for requesting verification, you will be asked for up to five links. In my case, I gave the URL of my blog (which, as I said, has a section explaining who I am, shows my real name, and also a picture of myself, so they can check the Twitter avatar, the photo ID as well). I also gave my LinkedIn profile, my Google+ profile. And I think that&rsquo;s all. Anything that proves you&rsquo;re who you claim to be can be useful.</p>
<p>Last but not least, there&rsquo;s a form field where you have to justify why you request becoming verified. In my case, I wanted cover the two &ldquo;aspects&rdquo; of my life: at day, I&rsquo;m a Developer Advocate for Google, and at night, I&rsquo;m working on the Apache Groovy open source project. For reference, here&rsquo;s the blurb that I used:</p>
<blockquote>
<p>Leading the Apache Groovy project, I&rsquo;m a spokesperson for the successful open source project, and for the ecosystem &amp; community around it. In my day life, I&rsquo;m also a Developer Advocate, at Google, on their Cloud Platform. Having my Twitter account verified would be an additional stamp of approval for my involvement in the Groovy community and with the company&rsquo;s product I advocate for.</p></blockquote>
<p>After my first attempt two months ago, I tried again over the weekend, and the next day, I got the email confirming my account was verified! Yay!</p>
<p>I hope this article is helpful. Don&rsquo;t hesitate to share your own tricks in the comments. At least, following the advice above, I was able to get verified. I have about 11K followers, but I&rsquo;ve seen people with less than half as many followers also become verified. So the number of followers is not everything. But I think paying attention to the quality of your profile is what ultimately pays off. So good luck with your verification process! And don&rsquo;t hesitate to share your own tips in the comments below.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Binge streaming web APIs with Ratpack, Cloud Ednpoints, App Engine Flex and Streamdata.io</title><link>https://glaforge.dev/talks/2016/11/15/binge-streaming-web-apis-with-ratpack-cloud-ednpoints-app-engine-flex-and-streamdata-io/</link><pubDate>Tue, 15 Nov 2016 16:40:32 +0100</pubDate><guid>https://glaforge.dev/talks/2016/11/15/binge-streaming-web-apis-with-ratpack-cloud-ednpoints-app-engine-flex-and-streamdata-io/</guid><description>&lt;p>At &lt;a href="https://devoxx.be/">Devoxx&lt;/a> last week, I had the chance to do a joint tools-in-action with my talented friend Audrey Neveu, titled Binge streaming you Web API:&lt;/p>
&lt;blockquote>
&lt;p>In a fast-paced fashion, to keep you awake after long University sessions, Audrey and Guillaume will set you up to create a Web API using Google Cloud Endpoints, and stream the content of the API in real-time with Streamdata.io. After a quick introduction to both technologies, they’ll build together both the backend and the front-end to interact live with the audience, through the Web or via a mobile app.&lt;/p></description><content:encoded>
<![CDATA[<p>At <a href="https://devoxx.be/">Devoxx</a> last week, I had the chance to do a joint tools-in-action with my talented friend Audrey Neveu, titled Binge streaming you Web API:</p>
<blockquote>
<p>In a fast-paced fashion, to keep you awake after long University sessions, Audrey and Guillaume will set you up to create a Web API using Google Cloud Endpoints, and stream the content of the API in real-time with Streamdata.io. After a quick introduction to both technologies, they’ll build together both the backend and the front-end to interact live with the audience, through the Web or via a mobile app.</p></blockquote>
<p>For the impatient, scroll down to the end of the article to access the slides presented, and view a recording of the video!</p>
<p>We split the presentation in two sections: 1) first of all, we obviously need to create and deploy a Web API, and then 2) to configure and use <a href="http://streamdata.io/">Streamdata.io</a> to stream the updates live, rather than poll the API endlessly.</p>
<p>For the purpose of our demo, Audrey and myself decided to surf on the theme of the conference, by publishing ourselves an API of the conference content, listing all the talks and speakers available.</p>
<p>As the content is pretty much static, we needed some data that would evolve in real-time as well. We added a voting capability, so that users could click on a little smiley to say if they&rsquo;re enjoying the talk or not. With the streaming capability, as soon as votes are taking place, we update the UI with the new vote results.</p>
<h2 id="implementing-my-api-with-ratpack">Implementing my API with Ratpack</h2>
<p>To build the API, I decided to go with the <a href="https://ratpack.io/">Ratpack framework</a>. As the Ratpack website states:</p>
<p>Ratpack is a set of Java libraries for building modern HTTP applications.</p>
<p>It provides just enough for writing practical, high performance, apps.</p>
<p>It is built on Java 8, Netty and reactive principles.</p>
<p>You can use Ratpack with Java 8, but there&rsquo;s also a nice Groovy wrapper. So with my <a href="http://www.groovy-lang.org/">Apache Groovy</a> hat on, I naturally chose to go with Groovy!</p>
<p>In the Groovy community, there&rsquo;s a tool called <a href="https://github.com/pledbrook/lazybones">Lazybones</a> which allows you to create template projects easily. And we&rsquo;re also using <a href="http://sdkman.io/">SDKman</a> for installing various SDKs, including SDKman.</p>
<p>If you don&rsquo;t have lazybones installed (but have SDKman), it&rsquo;s fairly easy to install:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sdk install lazybones
</span></span></code></pre></div><p>With both installed already on my machine, I just needed to create a new project with the Ratpack template:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>lazybones create ratpack
</span></span></code></pre></div><p>And I had my template project ready!</p>
<p>Gradle to the rescue to build the app</p>
<p>My Gradle script is pretty straightforward, using the Ratpack Groovy plugin, the Shadow plugin, etc. Nothing really fancy:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>buildscript <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    repositories <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        jcenter<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    dependencies <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        classpath <span style="color:#4070a0">&#34;io.ratpack:ratpack-gradle:1.4.4&#34;</span>
</span></span><span style="display:flex;"><span>        classpath <span style="color:#4070a0">&#34;com.github.jengelman.gradle.plugins:shadow:1.2.3&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>apply <span style="color:#002070;font-weight:bold">plugin:</span> <span style="color:#4070a0">&#34;io.ratpack.ratpack-groovy&#34;</span>
</span></span><span style="display:flex;"><span>apply <span style="color:#002070;font-weight:bold">plugin:</span> <span style="color:#4070a0">&#34;com.github.johnrengelman.shadow&#34;</span>
</span></span><span style="display:flex;"><span>apply <span style="color:#002070;font-weight:bold">plugin:</span> <span style="color:#4070a0">&#34;idea&#34;</span>
</span></span><span style="display:flex;"><span>apply <span style="color:#002070;font-weight:bold">plugin:</span> <span style="color:#4070a0">&#34;eclipse&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>repositories <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    jcenter<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>dependencies <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    runtime <span style="color:#4070a0">&#39;org.slf4j:slf4j-simple:1.7.21&#39;</span>
</span></span><span style="display:flex;"><span>    testCompile <span style="color:#4070a0">&#34;org.spockframework:spock-core:1.0-groovy-2.4&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>To build this app, I&rsquo;ll use the tar distribution target, which generates startup scripts to be launched from the command-line:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>./gradlew distTar
</span></span></code></pre></div><p>To run the app locally, you can use:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>./gradlew run
</span></span></code></pre></div><p>By default, it runs on port 5050.</p>
<p>The URL paths and methods</p>
<p>For developing my API, I created the following paths and methods:</p>
<ul>
<li>GET /api : to list all the talks available</li>
<li>GET /api/{day} : to restrict the list of talks to just one particular day</li>
<li>GET /api/talk/{id} : to view the details of a particular talk</li>
<li>POST /api/talk/{id}/vote/{vote} : to vote on a particular talk (negative, neutral, positive)</li>
<li>POST /import : to import a JSON dump of all the talks, but it&rsquo;s just used by me for uploading the initial content, so it&rsquo;s not really part of my API.</li>
</ul>
<h2 id="implementing-the-api">Implementing the API</h2>
<p>My <a href="https://ratpack.io/">Ratpack</a> app implementing this API (plus a few other URLs) spans a hundred lines of Groovy code or so (including blank lines, imports, and curly braces). The implementation is a bit naive as I&rsquo;m storing the talks / speakers data in memory, but I should have used a backend storage like <a href="https://cloud.google.com/datastore/">Cloud Datastore</a> or <a href="https://cloud.google.com/sql/">Cloud SQL</a>, potentially with Memcache in front. So the app won&rsquo;t scale well and data will not be synchronized across multiple instances running in parallel. For the sake of my demo though, that was sufficient!</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">ratpack.handling.RequestLogger</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.slf4j.LoggerFactory</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">static</span> ratpack<span style="color:#666">.</span><span style="color:#4070a0">groovy</span><span style="color:#666">.</span><span style="color:#4070a0">Groovy</span><span style="color:#666">.</span><span style="color:#4070a0">ratpack</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">static</span> ratpack<span style="color:#666">.</span><span style="color:#4070a0">jackson</span><span style="color:#666">.</span><span style="color:#4070a0">Jackson</span><span style="color:#666">.</span><span style="color:#4070a0">json</span> <span style="color:#007020;font-weight:bold">as</span> toJson
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">static</span> ratpack<span style="color:#666">.</span><span style="color:#4070a0">jackson</span><span style="color:#666">.</span><span style="color:#4070a0">Jackson</span><span style="color:#666">.</span><span style="color:#4070a0">fromJson</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> log <span style="color:#666">=</span> LoggerFactory<span style="color:#666">.</span><span style="color:#4070a0">getLogger</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;Devoxx&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> allTalks <span style="color:#666">=</span> <span style="color:#666">[]</span>
</span></span><span style="display:flex;"><span>ratpack <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    handlers <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        all<span style="color:#666">(</span>RequestLogger<span style="color:#666">.</span><span style="color:#4070a0">ncsa</span><span style="color:#666">())</span>
</span></span><span style="display:flex;"><span>        post<span style="color:#666">(</span><span style="color:#4070a0">&#39;import&#39;</span><span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>            log<span style="color:#666">.</span><span style="color:#4070a0">info</span> <span style="color:#4070a0">&#34;Importing talks dump&#34;</span>
</span></span><span style="display:flex;"><span>            byContent <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                json <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                    parse<span style="color:#666">(</span>fromJson<span style="color:#666">(</span>List<span style="color:#666">)).</span><span style="color:#4070a0">onError</span> <span style="color:#666">{</span> e <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>                        String msg <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Import failed: $e&#34;</span>
</span></span><span style="display:flex;"><span>                        log<span style="color:#666">.</span><span style="color:#4070a0">error</span> msg
</span></span><span style="display:flex;"><span>                        response<span style="color:#666">.</span><span style="color:#4070a0">status</span><span style="color:#666">(</span><span style="color:#40a070">400</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>                        render <span style="color:#06287e">toJson</span><span style="color:#666">([</span><span style="color:#002070;font-weight:bold">status:</span> <span style="color:#4070a0">&#34;Import failed: $e&#34;</span><span style="color:#666">])</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#666">}.</span><span style="color:#4070a0">then</span> <span style="color:#666">{</span> talks <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>                        allTalks <span style="color:#666">=</span> talks
</span></span><span style="display:flex;"><span>                        log<span style="color:#666">.</span><span style="color:#4070a0">info</span> <span style="color:#4070a0">&#34;Loaded ${allTalks.size()} talks&#34;</span>
</span></span><span style="display:flex;"><span>                        render <span style="color:#06287e">toJson</span><span style="color:#666">([</span><span style="color:#002070;font-weight:bold">status:</span> <span style="color:#4070a0">&#39;Import successful&#39;</span><span style="color:#666">])</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>                <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>            <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>        prefix<span style="color:#666">(</span><span style="color:#4070a0">&#39;api&#39;</span><span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>            prefix<span style="color:#666">(</span><span style="color:#4070a0">&#39;talk&#39;</span><span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                post<span style="color:#666">(</span><span style="color:#4070a0">&#39;:id/vote/:vote&#39;</span><span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#902000">def</span> aTalk <span style="color:#666">=</span> allTalks<span style="color:#666">.</span><span style="color:#4070a0">find</span> <span style="color:#666">{</span> it<span style="color:#666">.</span><span style="color:#4070a0">id</span> <span style="color:#666">==</span> pathTokens<span style="color:#666">.</span><span style="color:#4070a0">id</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>aTalk<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                        <span style="color:#902000">def</span> msg <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Voted $pathTokens.vote on talk $pathTokens.id&#34;</span><span style="color:#666">.</span><span style="color:#4070a0">toString</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>                        <span style="color:#007020;font-weight:bold">switch</span> <span style="color:#666">(</span>pathTokens<span style="color:#666">.</span><span style="color:#4070a0">vote</span><span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                            <span style="color:#007020;font-weight:bold">case</span> <span style="color:#4070a0">&#34;negative&#34;</span><span style="color:#666">:</span>
</span></span><span style="display:flex;"><span>                                aTalk<span style="color:#666">.</span><span style="color:#4070a0">reactions</span><span style="color:#666">.</span><span style="color:#4070a0">negative</span> <span style="color:#666">+=</span> <span style="color:#40a070">1</span>
</span></span><span style="display:flex;"><span>                                log<span style="color:#666">.</span><span style="color:#4070a0">info</span> msg
</span></span><span style="display:flex;"><span>                                render <span style="color:#06287e">toJson</span><span style="color:#666">([</span><span style="color:#002070;font-weight:bold">status:</span> msg<span style="color:#666">])</span>
</span></span><span style="display:flex;"><span>                                <span style="color:#007020;font-weight:bold">break</span>
</span></span><span style="display:flex;"><span>                            <span style="color:#007020;font-weight:bold">case</span> <span style="color:#4070a0">&#34;neutral&#34;</span><span style="color:#666">:</span>
</span></span><span style="display:flex;"><span>                                aTalk<span style="color:#666">.</span><span style="color:#4070a0">reactions</span><span style="color:#666">.</span><span style="color:#4070a0">neutral</span> <span style="color:#666">+=</span> <span style="color:#40a070">1</span>
</span></span><span style="display:flex;"><span>                                log<span style="color:#666">.</span><span style="color:#4070a0">info</span> msg
</span></span><span style="display:flex;"><span>                                render <span style="color:#06287e">toJson</span><span style="color:#666">([</span><span style="color:#002070;font-weight:bold">status:</span> msg<span style="color:#666">])</span>
</span></span><span style="display:flex;"><span>                                <span style="color:#007020;font-weight:bold">break</span>
</span></span><span style="display:flex;"><span>                            <span style="color:#007020;font-weight:bold">case</span> <span style="color:#4070a0">&#34;positive&#34;</span><span style="color:#666">:</span>
</span></span><span style="display:flex;"><span>                                aTalk<span style="color:#666">.</span><span style="color:#4070a0">reactions</span><span style="color:#666">.</span><span style="color:#4070a0">positive</span> <span style="color:#666">+=</span> <span style="color:#40a070">1</span>
</span></span><span style="display:flex;"><span>                                log<span style="color:#666">.</span><span style="color:#4070a0">info</span> msg
</span></span><span style="display:flex;"><span>                                render <span style="color:#06287e">toJson</span><span style="color:#666">([</span><span style="color:#002070;font-weight:bold">status:</span> msg<span style="color:#666">])</span>
</span></span><span style="display:flex;"><span>                                <span style="color:#007020;font-weight:bold">break</span>
</span></span><span style="display:flex;"><span>                            <span style="color:#007020;font-weight:bold">default</span><span style="color:#666">:</span>
</span></span><span style="display:flex;"><span>                                response<span style="color:#666">.</span><span style="color:#4070a0">status</span><span style="color:#666">(</span><span style="color:#40a070">400</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>                                msg <span style="color:#666">=</span> <span style="color:#4070a0">&#34;&#39;${pathTokens.vote}&#39; is not a valid vote&#34;</span><span style="color:#666">.</span><span style="color:#4070a0">toString</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>                                log<span style="color:#666">.</span><span style="color:#4070a0">info</span> msg
</span></span><span style="display:flex;"><span>                                render <span style="color:#06287e">toJson</span><span style="color:#666">([</span><span style="color:#002070;font-weight:bold">status:</span> msg<span style="color:#666">])</span>
</span></span><span style="display:flex;"><span>                        <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">else</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                        response<span style="color:#666">.</span><span style="color:#4070a0">status</span><span style="color:#666">(</span><span style="color:#40a070">404</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>                        render <span style="color:#06287e">toJson</span><span style="color:#666">([</span><span style="color:#002070;font-weight:bold">status:</span> <span style="color:#4070a0">&#34;Talk $pathTokens.id not found&#34;</span><span style="color:#666">.</span><span style="color:#4070a0">toString</span><span style="color:#666">()])</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>                <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>                get<span style="color:#666">(</span><span style="color:#4070a0">&#39;:id&#39;</span><span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#902000">def</span> aTalk <span style="color:#666">=</span> allTalks<span style="color:#666">.</span><span style="color:#4070a0">find</span> <span style="color:#666">{</span> it<span style="color:#666">.</span><span style="color:#4070a0">id</span> <span style="color:#666">==</span> pathTokens<span style="color:#666">.</span><span style="color:#4070a0">id</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>aTalk<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                        log<span style="color:#666">.</span><span style="color:#4070a0">info</span> <span style="color:#4070a0">&#34;Found talk: $pathTokens.id&#34;</span>
</span></span><span style="display:flex;"><span>                        render <span style="color:#06287e">toJson</span><span style="color:#666">(</span>aTalk<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">else</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                        String msg <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Talk $pathTokens.id not found&#34;</span>
</span></span><span style="display:flex;"><span>                        log<span style="color:#666">.</span><span style="color:#4070a0">info</span> msg
</span></span><span style="display:flex;"><span>                        response<span style="color:#666">.</span><span style="color:#4070a0">status</span><span style="color:#666">(</span><span style="color:#40a070">404</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>                        render <span style="color:#06287e">toJson</span><span style="color:#666">([</span><span style="color:#002070;font-weight:bold">status:</span> msg<span style="color:#666">])</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>                <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>            <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>            get<span style="color:#666">(</span><span style="color:#4070a0">&#39;:day&#39;</span><span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                <span style="color:#902000">def</span> talksPerDay <span style="color:#666">=</span> allTalks<span style="color:#666">.</span><span style="color:#4070a0">findAll</span> <span style="color:#666">{</span> it<span style="color:#666">.</span><span style="color:#4070a0">day</span> <span style="color:#666">==</span> pathTokens<span style="color:#666">.</span><span style="color:#4070a0">day</span> <span style="color:#666">}.</span><span style="color:#4070a0">collect</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                    it<span style="color:#666">.</span><span style="color:#4070a0">subMap</span><span style="color:#666">(</span>it<span style="color:#666">.</span><span style="color:#4070a0">keySet</span><span style="color:#666">()</span> <span style="color:#666">-</span> <span style="color:#4070a0">&#39;summary&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>                <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>                <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>talksPerDay<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                    render <span style="color:#06287e">toJson</span><span style="color:#666">(</span>talksPerDay<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>                <span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">else</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                    response<span style="color:#666">.</span><span style="color:#4070a0">status</span><span style="color:#666">(</span><span style="color:#40a070">404</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>                    render <span style="color:#06287e">toJson</span><span style="color:#666">([</span><span style="color:#002070;font-weight:bold">status:</span> <span style="color:#4070a0">&#34;Invalid day, or no talks found for: $pathTokens.day&#34;</span><span style="color:#666">.</span><span style="color:#4070a0">toString</span><span style="color:#666">()])</span>
</span></span><span style="display:flex;"><span>                <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>            <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>            get <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                render <span style="color:#06287e">toJson</span><span style="color:#666">(</span>request<span style="color:#666">.</span><span style="color:#4070a0">queryParams</span><span style="color:#666">.</span><span style="color:#4070a0">full</span> <span style="color:#666">?</span> allTalks <span style="color:#666">:</span> allTalks<span style="color:#666">.</span><span style="color:#4070a0">collect</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                    it<span style="color:#666">.</span><span style="color:#4070a0">subMap</span><span style="color:#666">(</span>it<span style="color:#666">.</span><span style="color:#4070a0">keySet</span><span style="color:#666">()</span> <span style="color:#666">-</span> <span style="color:#4070a0">&#39;summary&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>                <span style="color:#666">})</span>
</span></span><span style="display:flex;"><span>            <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Some interesting points about this code:</p>
<ul>
<li>use of an NCSA-compliant request logger to log all API calls (following the NCSA usual output pattern) and a dedicated logger for important events, and you&rsquo;re able to watch both those kind of logs in the Stackdriver logging in the cloud console</li>
<li>the use of prefix(&rsquo;&hellip;&rsquo;) to factor common path parts</li>
<li>Jackson is being used both for parsing (the input file containing the initial list of talks) as well as for output for rendering the JSON payloads</li>
<li>see how we use byContent / json to handle the requests coming up with content-type of application/json</li>
<li>the rest is essentially error handling, collection filtering, etc.</li>
</ul>
<h2 id="containerizing-the-app">Containerizing the app</h2>
<p>Ratpack requires JDK 8 to run, and it&rsquo;s not based on servlets, I went with <a href="https://cloud.google.com/appengine/docs/flexible/">Google App Engine Flex</a>, which allows me to run JDK 8 / Java 8 apps which can be based on networking toolkits like <a href="http://netty.io/">Netty</a>.</p>
<p>I mentioned we&rsquo;re using the distTar target to build a distribution of the application, and that&rsquo;s what we&rsquo;ll point our Dockerfile at, as App Engine Flex allows you to customize Docker images for bundling and running your app:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-dockerfile" data-lang="dockerfile"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#4070a0"> gcr.io/google_appengine/openjdk8</span><span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">VOLUME</span><span style="color:#4070a0"> /tmp</span><span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">RUN</span> mkdir -p /app/endpoints<span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">ADD</span> service.json /app/endpoints<span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">ADD</span> build/distributions/devoxx-reactions.tar /<span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">ENV</span> <span style="color:#bb60d5">JAVA_OPTS</span><span style="color:#666">=</span><span style="color:#4070a0">&#39;-Dratpack.port=8080 -Djava.security.egd=file:/dev/./urandom&#39;</span><span style="">
</span></span></span><span style="display:flex;"><span><span style=""></span><span style="color:#007020;font-weight:bold">ENTRYPOINT</span> [<span style="color:#4070a0">&#34;/devoxx-reactions/bin/devoxx-reactions&#34;</span>]<span style="">
</span></span></span></code></pre></div><p>I&rsquo;m using the dedicated Open JDK 8 image for App Engine, from the <a href="https://cloud.google.com/container-registry/">Google Container Registry</a>. I&rsquo;m specifying the port for my app, the entry point to the startup scripts. You&rsquo;ll notice the lines about &ldquo;endpoints&rdquo; and &ldquo;service.json&rdquo;, and I&rsquo;ll come to it in a minute: it&rsquo;s because I&rsquo;m using <a href="https://cloud.google.com/endpoints/">Google Cloud Endpoints</a> to expose and manage my API!</p>
<p>At this point, to feel safer, you can double check that your app is running under docker with something like:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>./gradlew distTar
</span></span><span style="display:flex;"><span>docker build -t devoxx-reactions-image .
</span></span><span style="display:flex;"><span>docker run -p 127.0.0.1:8080:8080 -it devoxx-reactions-image
</span></span></code></pre></div><p>The app is running on port 8080 of your localhost. We&rsquo;ll see later on how to test it with curl, and how to load the sample data that we&rsquo;ve prepared in public/data/talks.json.</p>
<h2 id="gcloud-to-set-up-our-projects">GCloud, to set up our projects</h2>
<p>As we&rsquo;re going to use Cloud Endpoints on App Engine Flex, it&rsquo;s time that I start setting things up with the gcloud SDK:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud init
</span></span><span style="display:flex;"><span>gcloud components update
</span></span><span style="display:flex;"><span>gcloud components install beta
</span></span><span style="display:flex;"><span>gcloud config <span style="color:#007020">set</span> project <span style="color:#666">[</span>YOUR_PROJECT_ID<span style="color:#666">]</span>
</span></span></code></pre></div><h2 id="about-google-cloud-endpoints">About Google Cloud Endpoints</h2>
<p>Google Cloud Platform offers a particular service for managing Web APIs called <a href="https://cloud.google.com/endpoints/">Endpoints</a>. With Endpoints, you&rsquo;re able to monitor your API (see what endpoints, methods, are called, with some nice graphs and stats), to secure your API (with different kind of authentication like Firebase authentication, JSON Web Tokens, API keys, etc.), to scale it.</p>
<p>Speaking of scaling, I&rsquo;m using App Engine Flex here as my deployment target, but it&rsquo;s possible to use Compute Engine or Container Engine as well. Interestingly, you can use Endpoints API management with an API hosted in a third-party cloud, as well as on premises too!</p>
<p>The management aspect of the API is done thanks to a proxy, called the Endpoints Service Proxy, which is implemented on top of NGINX (and which will be open sourced). And to continue on scaling aspect of the story, it&rsquo;s interesting to note that this proxy is living along your app (in its own container). If your API needs to scale across several machines, instead of one giant middle proxy somewhere, your ESP will be duplicated the same way. So there&rsquo;s no single point of failure with a gigantic proxy, but it also means that the latency is super low (below 1 ms usually), because the proxy is as close as possible to your API, without needing any additional costly network hop.</p>
<p>The last interesting aspect about Cloud Endpoints that I&rsquo;d like to mention is that your API contract is defined using <a href="https://openapis.org/">OpenAPI Specs</a> (formerly known as Swagger). So it doesn&rsquo;t matter which language, framework, tech stack you&rsquo;re using to implement your API: as long as you&rsquo;re able to describe your API with an OpenAPI Specification, you&rsquo;re good to go!</p>
<h2 id="specifying-the-contract-of-our-api-with-openapi-spec">Specifying the contract of our API with OpenAPI Spec</h2>
<p>I mentioned before the various resources and methods we&rsquo;re using for our API, and we&rsquo;ll encode these in the form of a contract, using the OpenAPI Spec definition format. In addition to the various resources and methods, we should also define the payloads that will be exchanged: basically, a Talk, a Result, a list of talks. We must define the different status codes for each kind of response. Here&rsquo;s what my specification looks like:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#0e84b5;font-weight:bold">---</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">swagger</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;2.0&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">info</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">description</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Consult the Devoxx schedule and vote on your favorite talks.&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">version</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;1.0.0&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">title</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Devoxx Reactions&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">contact</span>:<span style="color:#bbb"> </span>{}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">host</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;devoxx-reactions.appspot.com&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">schemes</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>- <span style="color:#4070a0">&#34;http&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">paths</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">/import</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">post</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">summary</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Import a dump of the lists of talks&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">operationId</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;ImportTalks&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">consumes</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#4070a0">&#34;application/json&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">produces</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#4070a0">&#34;application/json&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">parameters</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">name</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;talkList&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">in</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;body&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">required</span>:<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">true</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">schema</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">$ref</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;#/definitions/TalkList&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">responses</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">200</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">description</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Status 200&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">schema</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">$ref</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;#/definitions/Result&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">400</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">description</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Status 400&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">schema</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">$ref</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;#/definitions/Result&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">/api</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">get</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">summary</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Get the list of talks&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">operationId</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;GetAllTalks&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">produces</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#4070a0">&#34;application/json&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">parameters</span>:<span style="color:#bbb"> </span>[]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">responses</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">200</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">description</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Status 200&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">schema</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;array&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">items</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span><span style="color:#062873;font-weight:bold">$ref</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;#/definitions/Talk&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>/api/talk/{talk}:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">get</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">summary</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Get a particular talk&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">operationId</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;GetOneTalk&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">produces</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#4070a0">&#34;application/json&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">parameters</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">name</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;talk&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">in</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;path&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">required</span>:<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">true</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;string&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">responses</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">200</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">description</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Status 200&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">schema</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">$ref</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;#/definitions/Talk&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">404</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">description</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Status 404&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">schema</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">$ref</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;#/definitions/Result&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>/api/talk/{talk}/vote/{vote}:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">post</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">summary</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Vote for a talk&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">operationId</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;VoteOnTalk&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">produces</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#4070a0">&#34;application/json&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">parameters</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">name</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;talk&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">in</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;path&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">required</span>:<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">true</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;string&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">name</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;vote&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">in</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;path&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">description</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;The vote can be \&#34;negative\&#34;, \&#34;neutral\&#34; or \&#34;positive\&#34;&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">required</span>:<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">true</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;string&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">responses</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">200</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">description</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Status 200&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">schema</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">$ref</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;#/definitions/Result&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">400</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">description</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Status 400&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">schema</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">$ref</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;#/definitions/Result&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">404</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">description</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Status 404&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">schema</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">$ref</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;#/definitions/Result&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>/api/{day}:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">get</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">summary</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Get the talks for a particular day&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">operationId</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;GetTalksPerDay&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">produces</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#4070a0">&#34;application/json&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">parameters</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>- <span style="color:#062873;font-weight:bold">name</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;day&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">in</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;path&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">required</span>:<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">true</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;string&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">responses</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">200</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">description</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Status 200&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">schema</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;array&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">items</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">              </span><span style="color:#062873;font-weight:bold">$ref</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;#/definitions/Talk&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">404</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">description</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Status 404&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">schema</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">$ref</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;#/definitions/Result&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#062873;font-weight:bold">definitions</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">Result</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;object&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">required</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#4070a0">&#34;status&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">properties</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">status</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;string&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">description</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;Voting results&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">Talk</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;object&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">required</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#4070a0">&#34;day&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#4070a0">&#34;fromTime&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#4070a0">&#34;fromTimeMillis&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#4070a0">&#34;id&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#4070a0">&#34;reactions&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#4070a0">&#34;speakers&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#4070a0">&#34;talkType&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#4070a0">&#34;title&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#4070a0">&#34;toTime&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#4070a0">&#34;toTimeMillis&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>- <span style="color:#4070a0">&#34;track&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">properties</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">day</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;string&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">fromTime</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;string&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">fromTimeMillis</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;integer&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">format</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;int64&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">id</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;string&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">description</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">reactions</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;object&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">properties</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">negative</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;integer&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">format</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;int32&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">neutral</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;integer&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">format</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;int32&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">positive</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;integer&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">format</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;int32&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">required</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#4070a0">&#34;negative&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#4070a0">&#34;neutral&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#4070a0">&#34;positive&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">room</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;string&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">speakers</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;array&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">items</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;string&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">summary</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;string&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">talkType</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;string&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">title</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;string&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">toTime</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;string&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">toTimeMillis</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;integer&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">format</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;int64&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">track</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;object&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">properties</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">title</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;string&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span><span style="color:#062873;font-weight:bold">trackId</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;string&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#062873;font-weight:bold">required</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#4070a0">&#34;title&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>- <span style="color:#4070a0">&#34;trackId&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">description</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;A talk representation&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span><span style="color:#062873;font-weight:bold">TalkList</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">type</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;array&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">items</span>:<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span><span style="color:#062873;font-weight:bold">$ref</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;#/definitions/Talk&#34;</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#062873;font-weight:bold">description</span>:<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;A list of talks&#34;</span><span style="color:#bbb">
</span></span></span></code></pre></div><p>You can write your API specifications using either JSON or YAML. I chose YAML because it&rsquo;s a bit easier to read, for the human eye, and still as much readable for the computer as well.</p>
<p>There&rsquo;s one extra step for instructing Cloud Endpoints about our API definition: I needed to convert my OpenAPI Spec into the service definition format used internally by Cloud Endpoints, thanks to the following command:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud beta service-management convert-config swagger20.yaml service.json
</span></span></code></pre></div><h2 id="deploying-on-app-engine-flex">Deploying on App Engine Flex</h2>
<p>To deploy on App Engine and use Cloud Endpoints, we&rsquo;re going to use the <a href="https://cloud.google.com/sdk/">gcloud</a> command-line tool again:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gcloud beta app deploy
</span></span></code></pre></div><p>After a few minutes, your app / API should be available and be ready for serving.</p>
<h2 id="testing-our-api">Testing our API</h2>
<p>I have a JSON file with the bulk of the talks and their details, so I uploaded it with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -d @src/ratpack/public/data/talks.json -H <span style="color:#4070a0">&#39;Content-Type: application/json&#39;</span> http://devoxx-reactions.appspot.com/import
</span></span></code></pre></div><p>And then I was able to call my API with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl https://devoxx-reactions.appspot.com/api
</span></span><span style="display:flex;"><span>curl https://devoxx-reactions.appspot.com/api/monday
</span></span><span style="display:flex;"><span>curl https://devoxx-reactions.appspot.com/api/talk/HFW-0944
</span></span></code></pre></div><p>And to vote for a given talk with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -X POST https://devoxx-reactions.appspot.com/api/talk/XMX-6190/vote/positive
</span></span></code></pre></div><h2 id="managing-your-api">Managing your API</h2>
<p>When you&rsquo;re visiting your cloud console, you&rsquo;ll be able to see interesting statistics and graphs, about the usage of your API:</p>
<p><figure>
  <a href="#img-7f3bd035db12c4022c67394201344888">
    <img src="/img/binge-streaming/endpoints-manage-1.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-7f3bd035db12c4022c67394201344888">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/binge-streaming/endpoints-manage-1.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>

<figure>
  <a href="#img-36ca75d90fa2d6c206100f06c317fcf9">
    <img src="/img/binge-streaming/endpoints-manage-2.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-36ca75d90fa2d6c206100f06c317fcf9">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/binge-streaming/endpoints-manage-2.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h2 id="streaming-the-api">Streaming the API</h2>
<p>For the streaming part of the story, I&rsquo;ll let Audrey cover it! I focused on the backend, how she focused on the Polymer frontend, with a custom Streamdata component, that used the Streamdata proxy to get the patches representing the difference between consecutive calls to the backend. So when the votes were changing (but the rest of the talk details were left unchanged), Streamdata would send back to the front only the diff. In addition to keeping ongoing data exchanges low (in terms of size), the proxy is also able to take care of caching, so it also helps avoiding hitting the backend too often, potentially helping with scalability of the backend, by keeping the cache at the proxy level.</p>
<h2 id="slides-and-video-available">Slides and video available!</h2>
<p>You can watch the video online on Devoxx&rsquo; YouTube channel:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/VT4xsDCeDxE?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>And you can also have a closer look at the slides as well:</p>
<script async class="speakerdeck-embed" data-id="87a0f10990e343e29007ea32e49ba0a3" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Latest features of Google Cloud Platform</title><link>https://glaforge.dev/posts/2016/11/13/latest-features-of-google-cloud-platform/</link><pubDate>Sun, 13 Nov 2016 16:53:50 +0100</pubDate><guid>https://glaforge.dev/posts/2016/11/13/latest-features-of-google-cloud-platform/</guid><description>&lt;p>When you&amp;rsquo;re following a project, a company, a platform, you&amp;rsquo;re looking for the latest news, about the latest feature announcement, to take advantage of what&amp;rsquo;s coming up.&lt;/p>
&lt;p>Last time, I blogged about the &lt;a href="https://glaforge.dev/posts/2016/09/28/gcloud-informative-update-message/">gcloud command line tool&lt;/a>, which nicely shows you the latest updates since the last time you updated its components.&lt;/p>
&lt;p>If you go to the Google Cloud Platform website, you&amp;rsquo;ll see dedicated release notes pages for pretty much all products. For example, here are the release notes for:&lt;/p></description><content:encoded>
<![CDATA[<p>When you&rsquo;re following a project, a company, a platform, you&rsquo;re looking for the latest news, about the latest feature announcement, to take advantage of what&rsquo;s coming up.</p>
<p>Last time, I blogged about the <a href="https://glaforge.dev/posts/2016/09/28/gcloud-informative-update-message/">gcloud command line tool</a>, which nicely shows you the latest updates since the last time you updated its components.</p>
<p>If you go to the Google Cloud Platform website, you&rsquo;ll see dedicated release notes pages for pretty much all products. For example, here are the release notes for:</p>
<ul>
<li><a href="https://cloud.google.com/compute/docs/release-notes">Compute Engine</a></li>
<li><a href="https://cloud.google.com/storage/release-notes">Cloud Storage</a></li>
<li><a href="https://cloud.google.com/bigquery/release-notes">BigQuery</a></li>
<li>etc.</li>
</ul>
<p>I&rsquo;ve just discovered a new way to stay updated about what&rsquo;s new. If you go to the <a href="https://console.cloud.google.com/">Cloud Console</a>, click on the little vertical dots, and then preferences:</p>
<p><figure>
  <a href="#img-83a03d655537c582985137218d41e4a9">
    <img src="/img/gcp-new-features/gcp-features-1-prefs.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-83a03d655537c582985137218d41e4a9">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gcp-new-features/gcp-features-1-prefs.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Then, in the main panel, you&rsquo;ll see a new &ldquo;communication&rdquo; section, and if you click on the &ldquo;updates &amp; offers&rdquo;, you&rsquo;ll get a chance to select the option &ldquo;feature announcements&rdquo;:</p>
<p><figure>
  <a href="#img-e1808925c7a25f3debc4ebee240d6729">
    <img src="/img/gcp-new-features/gcp-features-2-announcements.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-e1808925c7a25f3debc4ebee240d6729">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gcp-new-features/gcp-features-2-announcements.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>You&rsquo;ll receive monthly emails about the feature announcements.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Quick intro to Google Cloud Platform for the Paris Ansible meetup</title><link>https://glaforge.dev/talks/2016/11/02/quick-intro-to-gcp-for-the-paris-ansible-meetup/</link><pubDate>Wed, 02 Nov 2016 16:57:50 +0100</pubDate><guid>https://glaforge.dev/talks/2016/11/02/quick-intro-to-gcp-for-the-paris-ansible-meetup/</guid><description>&lt;p>Tonight, Google France was hosting the Paris Ansible meetup, and I had the chance to play the Master of Ceremony, by introducing the speakers for the evening, as well as give a brief introduction to the Google Cloud Platform, as well as outlining where Ansible users and DevOps engineers might be interested in learning more.&lt;/p>
&lt;p>Here&amp;rsquo;s my quick overview of the Google Cloud Platform:&lt;/p>
&lt;script async class="speakerdeck-embed" data-id="b35eb9967bc34da9ad36cc66b988ad57" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js">&lt;/script></description><content:encoded>
<![CDATA[<p>Tonight, Google France was hosting the Paris Ansible meetup, and I had the chance to play the Master of Ceremony, by introducing the speakers for the evening, as well as give a brief introduction to the Google Cloud Platform, as well as outlining where Ansible users and DevOps engineers might be interested in learning more.</p>
<p>Here&rsquo;s my quick overview of the Google Cloud Platform:</p>
<script async class="speakerdeck-embed" data-id="b35eb9967bc34da9ad36cc66b988ad57" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Scaling a Swagger-based web API on Google Cloud Endpoints</title><link>https://glaforge.dev/talks/2016/10/27/scaling-a-swagger-based-web-api-on-google-cloud-endpoints/</link><pubDate>Thu, 27 Oct 2016 17:00:56 +0100</pubDate><guid>https://glaforge.dev/talks/2016/10/27/scaling-a-swagger-based-web-api-on-google-cloud-endpoints/</guid><description>&lt;p>I had the pleasure of presenting at the &lt;a href="http://nordicapis.com/events/2016-platform-summit/">Nordic APIs Platform Summit 2016&lt;/a> in Stockholm this week. I enjoyed the conference a lot, with great speakers and content, flawless organization, and nice interactions with the audience.&lt;/p>
&lt;p>For the last keynote of the conference, I had the chance to present about &lt;a href="https://cloud.google.com/endpoints/">Google Cloud Endpoints&lt;/a>, Google&amp;rsquo;s take on API management. I worked on a little &amp;ldquo;pancake&amp;rdquo;-powered demo, deploying a &lt;a href="https://ratpack.io/">Ratpack&lt;/a> application, in a Docker container, on &lt;a href="https://cloud.google.com/container-engine/">Google Container Engine&lt;/a>. I created an &lt;a href="https://openapis.org/">OpenAPI Specification&lt;/a> describing my Web API that served pancakes. And used the Extensible Service Proxy to receive the API calls for securing (with an API key), monitoring (through the Cloud Console) and scaling my Web API (thanks to the scaling capabilities of Container Engine). This demo will be the topic of some upcoming blog posts.&lt;/p></description><content:encoded>
<![CDATA[<p>I had the pleasure of presenting at the <a href="http://nordicapis.com/events/2016-platform-summit/">Nordic APIs Platform Summit 2016</a> in Stockholm this week. I enjoyed the conference a lot, with great speakers and content, flawless organization, and nice interactions with the audience.</p>
<p>For the last keynote of the conference, I had the chance to present about <a href="https://cloud.google.com/endpoints/">Google Cloud Endpoints</a>, Google&rsquo;s take on API management. I worked on a little &ldquo;pancake&rdquo;-powered demo, deploying a <a href="https://ratpack.io/">Ratpack</a> application, in a Docker container, on <a href="https://cloud.google.com/container-engine/">Google Container Engine</a>. I created an <a href="https://openapis.org/">OpenAPI Specification</a> describing my Web API that served pancakes. And used the Extensible Service Proxy to receive the API calls for securing (with an API key), monitoring (through the Cloud Console) and scaling my Web API (thanks to the scaling capabilities of Container Engine). This demo will be the topic of some upcoming blog posts.</p>
<p>In the meantime, here is the abstract of my talk:</p>
<h2 id="scale-a-swagger-based-web-api-with-google-cloud-endpoints">Scale a Swagger-based Web API with Google Cloud Endpoints</h2>
<blockquote>
<p>Web APIs are and more often specified with API definition languages like Swagger (now named Open API Spec), as it can help you generate nice interactive documentation, server skeletons, and client SDKs, mocks, and more, making it simpler to get started both producing and consuming an API.</p>
<p>In this session, Guillaume will demonstrate how to define a Web API with Swagger / Open API Spec, and scale it using Cloud Endpoints, on the Google Cloud Platform.</p></blockquote>
<p>And here are the slides I presented:</p>
<script async class="speakerdeck-embed" data-id="a06c96a7c36a44cba63031f4146b8fae" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Viewing my Groovy source files in Stackdriver's debug view</title><link>https://glaforge.dev/posts/2016/10/17/viewing-my-groovy-source-files-in-stackdriver-debug-view/</link><pubDate>Mon, 17 Oct 2016 17:22:55 +0100</pubDate><guid>https://glaforge.dev/posts/2016/10/17/viewing-my-groovy-source-files-in-stackdriver-debug-view/</guid><description>&lt;p>As I was working on a demo for one of my talks at &lt;a href="https://devoxx.be/">Devoxx&lt;/a>, I was encountering a bug in my &lt;a href="http://www.groovy-lang.org/">Groovy&lt;/a> code (a &lt;a href="http://gaelyk.appspot.com/">Gaelyk&lt;/a> app using &lt;a href="http://glide-gae.appspot.com/">Glide&lt;/a>). I had deployed a new version of my &lt;a href="https://cloud.google.com/appengine/">App Engine&lt;/a> app, changing some code to persist some data in the &lt;a href="https://cloud.google.com/datastore/">Datastore&lt;/a>. After those changes, I saw a trace in the logs:&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-af172d2a202d6067b4a0385f0fde4dcb">
&lt;img src="https://glaforge.dev/img/sd-debug-groovy/stackdriver-stacktrace.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-af172d2a202d6067b4a0385f0fde4dcb">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/sd-debug-groovy/stackdriver-stacktrace.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>Looks like there&amp;rsquo;s an error in receiveTweet.groovy on line 11. And there&amp;rsquo;s a link! Although I hadn&amp;rsquo;t linked the source code to the application, I was surprised to see this link. But I knew that Stackdriver is able to &lt;a href="https://cloud.google.com/debugger/docs/source-options">pick up sources&lt;/a> in different ways (from uploaded local files, from a Google code source repository, from Github or BitBucket, or with a &amp;ldquo;source capture&amp;rdquo;).&lt;/p></description><content:encoded>
<![CDATA[<p>As I was working on a demo for one of my talks at <a href="https://devoxx.be/">Devoxx</a>, I was encountering a bug in my <a href="http://www.groovy-lang.org/">Groovy</a> code (a <a href="http://gaelyk.appspot.com/">Gaelyk</a> app using <a href="http://glide-gae.appspot.com/">Glide</a>). I had deployed a new version of my <a href="https://cloud.google.com/appengine/">App Engine</a> app, changing some code to persist some data in the <a href="https://cloud.google.com/datastore/">Datastore</a>. After those changes, I saw a trace in the logs:</p>
<p><figure>
  <a href="#img-af172d2a202d6067b4a0385f0fde4dcb">
    <img src="/img/sd-debug-groovy/stackdriver-stacktrace.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-af172d2a202d6067b4a0385f0fde4dcb">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/sd-debug-groovy/stackdriver-stacktrace.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Looks like there&rsquo;s an error in receiveTweet.groovy on line 11. And there&rsquo;s a link! Although I hadn&rsquo;t linked the source code to the application, I was surprised to see this link. But I knew that Stackdriver is able to <a href="https://cloud.google.com/debugger/docs/source-options">pick up sources</a> in different ways (from uploaded local files, from a Google code source repository, from Github or BitBucket, or with a &ldquo;source capture&rdquo;).</p>
<p>And actually, clicking that link brought me to the debug view, offering me the different ways to link to or upload the source code. Conveniently, the source capture approach provided a command, using the <a href="https://cloud.google.com/sdk/gcloud/">gcloud</a> CLI, to link the sources with traces:</p>
<p><figure>
  <a href="#img-fda030e460ba16635c8dd75f741ad3cc">
    <img src="/img/sd-debug-groovy/stackdriver-gcloud-capture.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-fda030e460ba16635c8dd75f741ad3cc">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/sd-debug-groovy/stackdriver-gcloud-capture.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I then launched that command in my terminal, and I was able to see the trace along with my source code afterwards in the Web console:</p>
<p><figure>
  <a href="#img-fec04c06ee5d253eb7b7778fbffeda28">
    <img src="/img/sd-debug-groovy/stackdriver-code-small.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-fec04c06ee5d253eb7b7778fbffeda28">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/sd-debug-groovy/stackdriver-code-small.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>On the left side, I can see my Groovy source files, highlighting the offending script with the bug. In the middle column: at the bottom, the stacktrace, and at the top, the source code, with the line where the exception occurred highlighted in blue.</p>
<p>On the right, there&rsquo;s also the live debugger view! But I haven&rsquo;t played with it yet, but it&rsquo;s pretty powerful, as you can live debug a production app! Let&rsquo;s keep it for another post!</p>
<p>However, now with your Groovy hat on, you&rsquo;ll notice two things:</p>
<p><figure>
  <a href="#img-b463c5e1b47722061ae6cfcbd04dad18">
    <img src="/img/sd-debug-groovy/stackdriver-zoom-code.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-b463c5e1b47722061ae6cfcbd04dad18">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/sd-debug-groovy/stackdriver-zoom-code.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>The Groovy source code is not nicely colored! Syntax coloring is available for languages like Java, Go, Python, JavaScript, but alas, not (yet?) for Groovy!</p>
<p>The other funny thing was the red message on the right as well:</p>
<p><figure>
  <a href="#img-95a808a3f1caa27833525d53a9552a89">
    <img src="/img/sd-debug-groovy/stackdriver-red-message.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-95a808a3f1caa27833525d53a9552a89">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/sd-debug-groovy/stackdriver-red-message.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Although it says only files with .java extension are supported, it was nice to see that it was still showing my Groovy source code!</p>
<h2 id="summary">Summary</h2>
<p>It&rsquo;s pretty neat to be able to associate the code and the logs directly in the web interface, to quickly spot where problems are coming from. Of course, you can go back to your IDE or text editor to find out (and ultimately that&rsquo;s what you&rsquo;ll be doing) but it&rsquo;s pretty handy to quickly visualize the origin of the problem and start figuring out what the problem may be.</p>
<p>Also, as I said, I haven&rsquo;t tried the live production debugger, but it&rsquo;s quite a killer feature in my book to be able to introspect a running app. It&rsquo;s not always easy to figure out some problems locally, as your emulator is not the actually running infrastructure, your tests are mocking things out but are &ldquo;not like the real thing&rdquo;, so having the ability to dive deeper in the running system is pretty compelling!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>IP filtering access to your VMs on Google Cloud</title><link>https://glaforge.dev/posts/2016/10/03/ip-filtering-access-to-your-vms-on-google-cloud/</link><pubDate>Mon, 03 Oct 2016 17:25:56 +0100</pubDate><guid>https://glaforge.dev/posts/2016/10/03/ip-filtering-access-to-your-vms-on-google-cloud/</guid><description>&lt;p>How do you filter access to your VMs on Google Cloud Platform? During a discussion with a customer, I was asked this question: only certain IP addresses or a range of IP addresses should have access to a particular VM. Let&amp;rsquo;s see that in action!&lt;/p>
&lt;p>Let&amp;rsquo;s assume you already have an account on Google Cloud Platform, but if you don&amp;rsquo;t, don&amp;rsquo;t miss the &lt;a href="https://cloud.google.com/free-trial/">$300 credits for a free trial&lt;/a>! I created a new project, then navigated to the Compute Engine section to create a new VM instance. I used all the default parameters, except that I checked the checkbox for &amp;ldquo;Allow HTTP traffic&amp;rdquo;, at the bottom of the following screenshot:&lt;/p></description><content:encoded>
<![CDATA[<p>How do you filter access to your VMs on Google Cloud Platform? During a discussion with a customer, I was asked this question: only certain IP addresses or a range of IP addresses should have access to a particular VM. Let&rsquo;s see that in action!</p>
<p>Let&rsquo;s assume you already have an account on Google Cloud Platform, but if you don&rsquo;t, don&rsquo;t miss the <a href="https://cloud.google.com/free-trial/">$300 credits for a free trial</a>! I created a new project, then navigated to the Compute Engine section to create a new VM instance. I used all the default parameters, except that I checked the checkbox for &ldquo;Allow HTTP traffic&rdquo;, at the bottom of the following screenshot:</p>
<p><figure>
  <a href="#img-2ba8746532d6b70254ced66ae4425397">
    <img src="/img/misc/firewall-create-instance.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-2ba8746532d6b70254ced66ae4425397">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/firewall-create-instance.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>For the purpose of this demo, I went with allowing traffic first, and then updating the firewall rule, but the best approach (since you don&rsquo;t want to let users access this VM) is to not allow HTTP traffic, and add the right rule afterwards. But I wanted to check that the traffic was flowing through normally, and then updated the rule to check that, indeed, the traffic was filtered.</p>
<p>My VM server isn&rsquo;t doing anything useful at this point, so I should at least run some web app on it! Wearing my <a href="http://www.groovy-lang.org/">Groovy</a> hat on, I decided to write a quick Groovy script with the <a href="https://ratpack.io/">Ratpack</a> framework. Let&rsquo;s see how to setup our VM to serve a simple hello world!</p>
<p>Once your VM instance is instantiated, you&rsquo;ll see a little SSH link along your instance in the list of running VMs. You can click on it, and you&rsquo;ll be able to SSH into your running system. So what&rsquo;s the recipe to run a little hello world in Ratpack? I installed OpenJDK 8, <a href="http://sdkman.io/">SDKMan</a> (to install Groovy, but which needed unzip to be installed for itself), and Groovy, with the following steps:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo su -
</span></span><span style="display:flex;"><span>apt-get update
</span></span><span style="display:flex;"><span>apt-get install openjdk-8-jdk
</span></span><span style="display:flex;"><span>apt-get install unzip
</span></span><span style="display:flex;"><span>curl -s <span style="color:#4070a0">&#34;https://get.sdkman.io&#34;</span> | bash
</span></span><span style="display:flex;"><span><span style="color:#007020">source</span> <span style="color:#4070a0">&#34;/root/.sdkman/bin/sdkman-init.sh&#34;</span>
</span></span><span style="display:flex;"><span>sdk install groovy
</span></span><span style="display:flex;"><span><span style="color:#007020">exit</span>
</span></span><span style="display:flex;"><span>mkdir ratpack
</span></span><span style="display:flex;"><span><span style="color:#007020">cd</span> ratpack
</span></span><span style="display:flex;"><span>vim hello.groovy
</span></span></code></pre></div><p>Then I created the hello.groovy Ratpack server with the following code:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Grab</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;io.ratpack:ratpack-groovy:1.4.2&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Grab</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;org.slf4j:slf4j-simple:1.7.21&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">static</span> ratpack<span style="color:#666">.</span><span style="color:#4070a0">groovy</span><span style="color:#666">.</span><span style="color:#4070a0">Groovy</span><span style="color:#666">.</span><span style="color:#4070a0">ratpack</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>ratpack <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    serverConfig <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        port <span style="color:#40a070">80</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    handlers <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        get <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>            render <span style="color:#4070a0">&#34;Hello World!&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>And then, I was ready to fire it with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>groovy hello
</span></span></code></pre></div><p>If you go back to your Google Cloud console, in the list of running instance, you certainly noticed the column showing the &ldquo;External IP&rdquo; address of your sever? Now you just need to let your browser open it. So head over to <code>http://123.123.123.123/</code> (or whichever IP you got), and you should see the infamous <code>Hello World!</code> message!</p>
<p>So far so good, but what we really want is to prevent access to this machine from anywhere, except a particular IP or range of IP addresses. Let&rsquo;s see how to do that next.</p>
<p>Let&rsquo;s go to the <code>Networking &gt; Firewall rules</code>:</p>
<p><figure>
  <a href="#img-02add3446704aaf99aa700965ede587f">
    <img src="/img/ip-filter/firewall-edit-rule.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-02add3446704aaf99aa700965ede587f">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/ip-filter/firewall-edit-rule.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>We&rsquo;re going to update the first rule: <code>default-allow-http</code>. Instead of <code>allowing from any source</code> with the <code>0.0.0.0/0</code> IP range, we&rsquo;re going to use our own custom range. In our case, let&rsquo;s say my own external IP address is <code>111.111.111.111</code>, so I&rsquo;ll restrict the range to just this IP with entering <code>111.111.111.111/0</code> as <code>Source IP range</code>. Let&rsquo;s save the firewall, and let the platform apply that change to our deployment. Once the change has taken place, you&rsquo;ll still be able to access your server at <code>http://123.123.123.123/</code>, because only your own IP address is white listed basically. But if you try with any other address (from a co-worker&rsquo;s machine, etc.), normally, nobody else will be able to access the sever beside you.</p>
<p>Done!</p>
<p>And now, for the bonus points! I started playing with that last week, and made the mistake of letting my VM instance running, underutilized. And this afternoon, as I resumed working on this article, I watch the list of instances running, and what do I see? The console telling me my VM instance is under-utilized and that I could save money by using a smaller VM instead! Looks like Google Cloud doesn&rsquo;t want me to waste my money! Sweet!</p>
<p><figure>
  <a href="#img-1365257bb9c659d824b04781f7cd3499">
    <img src="/img/ip-filter/firewall-save-cost.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-1365257bb9c659d824b04781f7cd3499">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/ip-filter/firewall-save-cost.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>GCloud informative update message</title><link>https://glaforge.dev/posts/2016/09/28/gcloud-informative-update-message/</link><pubDate>Wed, 28 Sep 2016 17:30:28 +0100</pubDate><guid>https://glaforge.dev/posts/2016/09/28/gcloud-informative-update-message/</guid><description>&lt;p>I was playing with the &lt;a href="https://cloudplatform.googleblog.com/2016/08/never-leave-your-Java-IDE-with-Google-Cloud-Tools-for-IntelliJ-plugin.html">new IntelliJ IDEA plugin for Google App Engine&lt;/a> yesterday. The plugin depends on the &lt;a href="https://cloud.google.com/sdk/gcloud/">gcloud SDK&lt;/a> to do its work. And I started exploring gcloud a little bit more.&lt;/p>
&lt;p>I was experiencing some odd bug which prevented me to run my application locally with the App Engine&amp;rsquo;s local app server. It was a bug which was present in an old version of gcloud and its App Engine component, so I had to update the SDK and its App Engine Java component to fix it. No big deal, but what I wanted to highlight here was a little detail about that upgrade process.&lt;/p></description><content:encoded>
<![CDATA[<p>I was playing with the <a href="https://cloudplatform.googleblog.com/2016/08/never-leave-your-Java-IDE-with-Google-Cloud-Tools-for-IntelliJ-plugin.html">new IntelliJ IDEA plugin for Google App Engine</a> yesterday. The plugin depends on the <a href="https://cloud.google.com/sdk/gcloud/">gcloud SDK</a> to do its work. And I started exploring gcloud a little bit more.</p>
<p>I was experiencing some odd bug which prevented me to run my application locally with the App Engine&rsquo;s local app server. It was a bug which was present in an old version of gcloud and its App Engine component, so I had to update the SDK and its App Engine Java component to fix it. No big deal, but what I wanted to highlight here was a little detail about that upgrade process.</p>
<p>I love when SDKs give me information about what needs updating and what&rsquo;s new in the new versions I&rsquo;m using!</p>
<p>I&rsquo;ve been using <a href="http://sdkman.io/">SDKMan</a> for dealing with various SDK installations, like those for Groovy, Grails, Gradle, etc, and I&rsquo;ve always liked when it was telling me which SDK updates were available, what was new in SDKMan. And I&rsquo;m glad to see that gcloud behaves the same, and gives informative details about what&rsquo;s new. So let&rsquo;s see that in action.</p>
<p>First of all, while debugging my problem with a colleague, he asked me which versions of the SDK and the App Engine component I had. So I ran the following command:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ gcloud version
</span></span><span style="display:flex;"><span>Google Cloud SDK 119.0.0
</span></span><span style="display:flex;"><span>alpha 2016.01.12
</span></span><span style="display:flex;"><span>app-engine-java 1.9.38
</span></span><span style="display:flex;"><span>app-engine-python 1.9.38
</span></span><span style="display:flex;"><span>beta 2016.01.12
</span></span><span style="display:flex;"><span>bq 2.0.24
</span></span><span style="display:flex;"><span>bq-nix 2.0.24
</span></span><span style="display:flex;"><span>core 2016.07.21
</span></span><span style="display:flex;"><span>core-nix 2016.06.06
</span></span><span style="display:flex;"><span>gcloud 
</span></span><span style="display:flex;"><span>gsutil 4.19
</span></span><span style="display:flex;"><span>gsutil-nix 4.19
</span></span></code></pre></div><p>At the time of this writing, the latest version of gcloud was actually 127.0.0, but I had 119.0.0. And for the app-engine-java component, I had version 1.9.38 although 1.9.42 was available. So it was time to update!</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ gcloud components update
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Your current Cloud SDK version is: 119.0.0
</span></span><span style="display:flex;"><span>You will be upgraded to version: 127.0.0
</span></span><span style="display:flex;"><span>┌──────────────────────────────────────────────────────────┐
</span></span><span style="display:flex;"><span>│            These components will be updated.             │
</span></span><span style="display:flex;"><span>├─────────────────────────────────┬────────────┬───────────┤
</span></span><span style="display:flex;"><span>│               Name              │  Version   │    Size   │
</span></span><span style="display:flex;"><span>├─────────────────────────────────┼────────────┼───────────┤
</span></span><span style="display:flex;"><span>│ BigQuery Command Line Tool      │     2.0.24 │   &lt; <span style="color:#40a070">1</span> MiB │
</span></span><span style="display:flex;"><span>│ Cloud SDK Core Libraries        │ 2016.09.20 │   4.9 MiB │
</span></span><span style="display:flex;"><span>│ Cloud Storage Command Line Tool │       4.21 │   2.8 MiB │
</span></span><span style="display:flex;"><span>│ gcloud app Java Extensions      │     1.9.42 │ 135.6 MiB │
</span></span><span style="display:flex;"><span>│ gcloud app Python Extensions    │     1.9.40 │   7.2 MiB │
</span></span><span style="display:flex;"><span>└─────────────────────────────────┴────────────┴───────────┘
</span></span><span style="display:flex;"><span>The following release notes are new in this upgrade.
</span></span><span style="display:flex;"><span>Please <span style="color:#007020">read</span> carefully <span style="color:#007020;font-weight:bold">for</span> information about new features, breaking changes,
</span></span><span style="display:flex;"><span>and bugs fixed.  The latest full release notes can be viewed at:
</span></span><span style="display:flex;"><span>  https://cloud.google.com/sdk/release_notes
</span></span><span style="display:flex;"><span>127.0.0 <span style="color:#666">(</span>2016/09/21<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>  Google BigQuery
</span></span><span style="display:flex;"><span>      - New load/query option in BigQuery client to support schema update
</span></span><span style="display:flex;"><span>        within a load/query job.
</span></span><span style="display:flex;"><span>      - New query option in BigQuery client to specify query parameters in
</span></span><span style="display:flex;"><span>        Standard SQL.
</span></span><span style="display:flex;"><span>  Google Cloud Dataproc
</span></span><span style="display:flex;"><span>      - gcloud dataproc clusters create flag
</span></span><span style="display:flex;"><span>        --preemptible-worker-boot-disk-size can be used to specify future
</span></span><span style="display:flex;"><span>        preemptible VM boot disk size.
</span></span><span style="display:flex;"><span>  Google Container Engine
</span></span><span style="display:flex;"><span>      - Update kubectl to version 1.3.7.
</span></span><span style="display:flex;"><span>  Google Cloud ML
</span></span><span style="display:flex;"><span>      - New gcloud beta ml predict <span style="color:#007020">command</span> to <span style="color:#007020;font-weight:bold">do</span> online prediction.
</span></span><span style="display:flex;"><span>      - New gcloud beta ml <span style="color:#007020">jobs</span> submit prediction <span style="color:#007020">command</span> to submit batch
</span></span><span style="display:flex;"><span>        prediction job.
</span></span><span style="display:flex;"><span>  Google Cloud SQL
</span></span><span style="display:flex;"><span>      - New arguments to beta sql instances create/patch commands <span style="color:#007020;font-weight:bold">for</span> Cloud
</span></span><span style="display:flex;"><span>        SQL Second Generation instances:
</span></span><span style="display:flex;"><span>        ◆ --storage-size Sets storage size in GB.
</span></span><span style="display:flex;"><span>        ◆ --maintenance-release-channel Sets production or preview channel
</span></span><span style="display:flex;"><span>          <span style="color:#007020;font-weight:bold">for</span> maintenance window.
</span></span><span style="display:flex;"><span>        ◆ --maintenance-window-day Sets day of week <span style="color:#007020;font-weight:bold">for</span> maintenance window.
</span></span><span style="display:flex;"><span>        ◆ --maintenance-window-hour Sets hour of day <span style="color:#007020;font-weight:bold">for</span> maintenance window.
</span></span><span style="display:flex;"><span>        ◆ --maintenance-window-any <span style="color:#666">(</span>patch only<span style="color:#666">)</span> Clears maintenance window
</span></span><span style="display:flex;"><span>          setting.
</span></span><span style="display:flex;"><span><span style="color:#666">[</span>...<span style="color:#666">]</span>
</span></span></code></pre></div><p>I snipped the output to just show the details of the changes for the latest version of gcloud, but it showed me the actual changelog up until the version I had&hellip; and as I hadn&rsquo;t updated in a while, there was lots of improvements and fixes! But it&rsquo;s really nice to see what had changed, and sometimes, you can discover some gems you weren&rsquo;t even aware of!</p>
<p>So if you&rsquo;re working on some kind of SDK, with auto-update capabilities, be sure to provide a built-in changelog facility to help your users know what&rsquo;s new and improved!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>JavaOne 2016 sessions</title><link>https://glaforge.dev/posts/2016/09/14/javaone-2016-sessions/</link><pubDate>Wed, 14 Sep 2016 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2016/09/14/javaone-2016-sessions/</guid><description>&lt;p>Next week will be this time of the year where tons of Java developers are gathering &amp;amp; meeting in San Francisco for &lt;a href="https://www.oracle.com/javaone/index.html">JavaOne&lt;/a>. It&amp;rsquo;ll be my 10th edition or so, time flies!&lt;br />
This year, I&amp;rsquo;ll participate to a couple sessions:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Java and the Commoditization of Machine Intelligence&lt;/strong> [CON2291]&lt;br />
It&amp;rsquo;s a panel discussion with representative from IBM, Microsoft and Google to talk about Machine Learning APIs. I&amp;rsquo;ll be covering the ML APIs from Google Cloud Platform: Vision, Speech, Natural Language.&lt;/li>
&lt;li>&lt;strong>A Groovy Journey in Open Source&lt;/strong> [CON5932]&lt;br />
In this session, I&amp;rsquo;ll cover the history of the Apache Groovy project, and talk about the latest developments and new features.&lt;/li>
&lt;/ul>
&lt;p>Google colleagues will also be present to speak about:&lt;/p></description><content:encoded>
<![CDATA[<p>Next week will be this time of the year where tons of Java developers are gathering &amp; meeting in San Francisco for <a href="https://www.oracle.com/javaone/index.html">JavaOne</a>. It&rsquo;ll be my 10th edition or so, time flies!<br />
This year, I&rsquo;ll participate to a couple sessions:</p>
<ul>
<li><strong>Java and the Commoditization of Machine Intelligence</strong> [CON2291]<br />
It&rsquo;s a panel discussion with representative from IBM, Microsoft and Google to talk about Machine Learning APIs. I&rsquo;ll be covering the ML APIs from Google Cloud Platform: Vision, Speech, Natural Language.</li>
<li><strong>A Groovy Journey in Open Source</strong> [CON5932]<br />
In this session, I&rsquo;ll cover the history of the Apache Groovy project, and talk about the latest developments and new features.</li>
</ul>
<p>Google colleagues will also be present to speak about:</p>
<ul>
<li><strong>gRPC 101 for Java Developers</strong> [CON5750] by Ray Tsang</li>
<li><strong>Managing and Deploying Java-Based Applications and Services at Scale</strong> [CON5730] by Ray Tsang</li>
<li><strong>Hacking Hiring</strong> [BOF1459] by Elliotte Harold</li>
<li><strong>The Ultimate Build Tools Face-off</strong> [CON2270] with Dmitry Churbanau and Baruch Sadogursky</li>
<li><strong>RIA Technologies and Frameworks Panel</strong> [CON4675] with Kevin Nilson</li>
</ul>
<p>There are quite a few interesting Groovy ecosystem related talks on the agenda:</p>
<ul>
<li><strong>Improving Your Groovy Kung-Fu</strong> [CON1293] by Dierk König</li>
<li><strong>Groovy and Java 8: Making Java Better</strong> [CON3277] by Ken Kousen</li>
<li><strong>Spock: Test Well and Prosper</strong> [CON3273] by Ken Kousen</li>
<li><strong>Writing Groovy AST Transformations: Getting Practical in an Hour</strong> [CON1238] by Baruch Sadogursky</li>
<li><strong>Juggling Multiple Java Platforms and Jigsaw with Gradle</strong> [CON4832] by Cédric Champeau</li>
<li><strong>Maven Versus Gradle: Ready&hellip;Steady&hellip;Go!</strong> [CON2951] by Mert Caliskan &amp; Murat Yener</li>
<li><strong>Meet the Gradle Team</strong> [BOF6209] with Sterling Greene &amp; Cédric Champeau</li>
<li><strong>Faster Java EE Builds with Gradle</strong> [CON4921] by Ryan Cuprak</li>
<li><strong>Lightweight Developer Provisioning with Gradle</strong> [BOF5154] by Mario-Leander Reimer</li>
<li><strong>Making the Most of Your Gradle Build</strong> [CON6468] by Andrés Almiray</li>
<li><strong>Gradle Support in NetBeans: A State of the Union</strong> [CON6253] with Sven Reimers &amp; Martin Klähn</li>
<li><strong>A Practical RxJava Example with Ratpack</strong> [CON4044] by Laurent Doguin</li>
</ul>
<p>Lots of interesting content! I&rsquo;m really looking forward to meeting you there, in the hallways, to chat about <a href="https://cloud.google.com/">Google Cloud Platform</a> and <a href="http://www.groovy-lang.org/">Apache Groovy</a>!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Natural language API and JavaScript promises to bind them all</title><link>https://glaforge.dev/posts/2016/07/28/natural-language-api-and-javascript-promises-to-bind-them-all/</link><pubDate>Thu, 28 Jul 2016 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2016/07/28/natural-language-api-and-javascript-promises-to-bind-them-all/</guid><description>&lt;p>A bit of &lt;a href="https://glaforge.dev/posts/2016/07/27/web-scraping-and-rest-api-calls-on-app-engine-with-jsoup-and-groovy-wslite/">web scraping with Jsoup and REST API calls with groovy-wsclient&lt;/a> helped me build my latest demo with &lt;a href="http://glide-gae.appspot.com/">Glide&lt;/a> / &lt;a href="http://gaelyk.appspot.com/">Gaelyk&lt;/a> on &lt;a href="https://cloud.google.com/appengine/">App Engine&lt;/a>, but now, it&amp;rsquo;s time to look a bit deeper into the analysis of the White House speeches:&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-90d80696f1e24c7a793b415cb46126da">
&lt;img src="https://glaforge.dev/img/misc/whitehouse-speeches-630.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-90d80696f1e24c7a793b415cb46126da">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/whitehouse-speeches-630.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>I wanted to have a feel of how positive and negative sentences flow together in speeches. Looking at the rhetoric of those texts, you&amp;rsquo;d find some flows of generally neutral introduction, then posing the problem with some negativity connotation, then the climax trying to unfold the problems with positive solutions. Some other topics might be totally different, though, but I was curious to see how this played out on the corpus of texts from the speeches and remarks published by the &lt;a href="https://www.whitehouse.gov/briefing-room/speeches-and-remarks">White House press office&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>A bit of <a href="https://glaforge.dev/posts/2016/07/27/web-scraping-and-rest-api-calls-on-app-engine-with-jsoup-and-groovy-wslite/">web scraping with Jsoup and REST API calls with groovy-wsclient</a> helped me build my latest demo with <a href="http://glide-gae.appspot.com/">Glide</a> / <a href="http://gaelyk.appspot.com/">Gaelyk</a> on <a href="https://cloud.google.com/appengine/">App Engine</a>, but now, it&rsquo;s time to look a bit deeper into the analysis of the White House speeches:</p>
<p><figure>
  <a href="#img-90d80696f1e24c7a793b415cb46126da">
    <img src="/img/misc/whitehouse-speeches-630.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-90d80696f1e24c7a793b415cb46126da">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/whitehouse-speeches-630.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I wanted to have a feel of how positive and negative sentences flow together in speeches. Looking at the rhetoric of those texts, you&rsquo;d find some flows of generally neutral introduction, then posing the problem with some negativity connotation, then the climax trying to unfold the problems with positive solutions. Some other topics might be totally different, though, but I was curious to see how this played out on the corpus of texts from the speeches and remarks published by the <a href="https://www.whitehouse.gov/briefing-room/speeches-and-remarks">White House press office</a>.</p>
<h2 id="the-cloud-natural-language-api">The Cloud Natural Language API</h2>
<p>For that purpose, I used the <a href="https://cloud.google.com/natural-language/docs/">Cloud Natural Language API</a>:</p>
<ul>
<li><a href="https://cloud.google.com/natural-language/docs/basics#sentence-extraction">Split the text into sentences</a> thanks to the <a href="https://cloud.google.com/natural-language/reference/rest/v1beta1/documents/annotateText">text annotation</a> capability. The API can split sentences even further, of course, by word, to figure out verbs, nouns, and all components of sentences (POS: Part Of Speech tagging).</li>
<li>Define the <a href="https://cloud.google.com/natural-language/docs/sentiment-tutorial">sentiment</a> of sentences, with a polarity (negative to positive), and a magnitude (for the intensity of the sentiment expressed).</li>
<li><a href="https://cloud.google.com/natural-language/docs/basics#entity_analysis">Extract entities</a>, ie. finding people / organization / enterprise names, place locations, etc.</li>
</ul>
<p>Text annotation is important for better understanding text, for example to create more accurate language translations. Sentiment analysis can help brands track how their customers appreciate their products. And entity extraction can help figure out the topics of articles, who&rsquo;s mentioned, places where the action takes places, which is useful for further contextual search, like finding all the articles about Obama, all the speeches about Europe, etc. There&rsquo;s a wide applicability of those various services to provide more metadata, a better understanding for a given piece of text.</p>
<h2 id="asynchronously-calling-the-service-and-gathering-results">Asynchronously calling the service and gathering results</h2>
<p>Let&rsquo;s look back at my experiment. When I scrape the speeches, I actually get a list of paragraphs (initially enclosed in &lt;p&gt; tags basically). But I want to analyze the text sentence by sentence, so I need to use the text annotation capability to split all those paragraphs into sentences that I analyze individually.</p>
<p>Currently, the sentiment analysis works on one piece of text at a time. So you have to make one call per sentence! Hopefully an option might come to allow to send several pieces of text in a batch, or giving the sentiment per sentence for a big chunk of text, etc. But for now, it means I&rsquo;ll have to make p calls for my p paragraphs, and then n calls for all the sentences. those p + n calls might be expensive in terms of network traffic, but on the other hand, I can make the sentence coloring appear progressively, and asynchronously, by using JavaScript Promises and Fetch API, as I&rsquo;m making those calls from the client side. But it seems it&rsquo;s possible to <a href="https://developers.google.com/api-client-library/java/google-api-java-client/batch">batch requests with the Google API Client</a>, but I haven&rsquo;t tried that yet.</p>
<p>First of all, to simplify the code a bit, I&rsquo;ve created a helper function that calls my backend services calling the NL API, that wraps the usage of the Fetch API, and the promise handling to gather the JSON response:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span> callService <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">function</span> (url, key, value) {
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">var</span> query <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> URLSearchParams();
</span></span><span style="display:flex;"><span>    query.append(key, value);
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">return</span> fetch(url, {
</span></span><span style="display:flex;"><span>        method<span style="color:#666">:</span> <span style="color:#4070a0">&#39;POST&#39;</span>,
</span></span><span style="display:flex;"><span>        body<span style="color:#666">:</span> query
</span></span><span style="display:flex;"><span>    }).then(<span style="color:#007020;font-weight:bold">function</span> (resp) {
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">return</span> resp.json();
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>I use the URLSearchParams object to pass my query parameter. The handy json() method on the response gives me the data structure resulting from the call. I&rsquo;m going to reuse that callService function in the following snippets:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>callService(<span style="color:#4070a0">&#39;/content&#39;</span>, <span style="color:#4070a0">&#39;url&#39;</span>, e.value).then(<span style="color:#007020;font-weight:bold">function</span> (paragraphs) {
</span></span><span style="display:flex;"><span>    paragraphs.forEach(<span style="color:#007020;font-weight:bold">function</span> (para, paraIdx) {
</span></span><span style="display:flex;"><span>        z(<span style="color:#4070a0">&#39;#output&#39;</span>).append(<span style="color:#4070a0">&#39;&lt;p id=&#34;para&#39;</span> <span style="color:#666">+</span> paraIdx <span style="color:#666">+</span> <span style="color:#4070a0">&#39;&#34;&gt;&#39;</span> <span style="color:#666">+</span> para <span style="color:#666">+</span> <span style="color:#4070a0">&#39;&lt;/p&gt;&#39;</span>);
</span></span><span style="display:flex;"><span>        callService(<span style="color:#4070a0">&#39;/sentences&#39;</span>, <span style="color:#4070a0">&#39;content&#39;</span>, para).then(<span style="color:#007020;font-weight:bold">function</span> (data) {
</span></span><span style="display:flex;"><span>            <span style="color:#007020;font-weight:bold">var</span> sentences <span style="color:#666">=</span> data.sentences.map(<span style="color:#007020;font-weight:bold">function</span> (sentence) {
</span></span><span style="display:flex;"><span>                <span style="color:#007020;font-weight:bold">return</span> sentence.text.content;
</span></span><span style="display:flex;"><span>            });
</span></span><span style="display:flex;"><span>            <span style="color:#007020;font-weight:bold">return</span> <span style="color:#007020">Promise</span>.all(sentences.map(<span style="color:#007020;font-weight:bold">function</span> (sentence) {
</span></span><span style="display:flex;"><span>                <span style="color:#007020;font-weight:bold">return</span> callService(<span style="color:#4070a0">&#39;/sentence&#39;</span>, <span style="color:#4070a0">&#39;content&#39;</span>, sentence).then(<span style="color:#007020;font-weight:bold">function</span> (sentenceSentiment) {
</span></span><span style="display:flex;"><span>                    <span style="color:#007020;font-weight:bold">var</span> polarity <span style="color:#666">=</span> sentenceSentiment.documentSentiment.polarity;
</span></span><span style="display:flex;"><span>                    <span style="color:#007020;font-weight:bold">var</span> magnitude <span style="color:#666">=</span> sentenceSentiment.documentSentiment.magnitude;
</span></span><span style="display:flex;"><span>                    <span style="color:#007020;font-weight:bold">return</span> {
</span></span><span style="display:flex;"><span>                        sentence<span style="color:#666">:</span> sentence,
</span></span><span style="display:flex;"><span>                        polarity<span style="color:#666">:</span> polarity,
</span></span><span style="display:flex;"><span>                        magnitude<span style="color:#666">:</span> magnitude
</span></span><span style="display:flex;"><span>                    }
</span></span><span style="display:flex;"><span>                });
</span></span><span style="display:flex;"><span>            }));
</span></span><span style="display:flex;"><span>        }).then(<span style="color:#007020;font-weight:bold">function</span> (allSentiments) {
</span></span><span style="display:flex;"><span>            <span style="color:#007020;font-weight:bold">var</span> coloredSentences <span style="color:#666">=</span> allSentiments.map(<span style="color:#007020;font-weight:bold">function</span> (sentiment) {
</span></span><span style="display:flex;"><span>                <span style="color:#007020;font-weight:bold">var</span> hsl <span style="color:#666">=</span> <span style="color:#4070a0">&#39;hsl(&#39;</span> <span style="color:#666">+</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#007020">Math</span>.floor((sentiment.polarity <span style="color:#666">+</span> <span style="color:#40a070">1</span>) <span style="color:#666">*</span> <span style="color:#40a070">60</span>) <span style="color:#666">+</span> <span style="color:#4070a0">&#39;, &#39;</span> <span style="color:#666">+</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#007020">Math</span>.min(<span style="color:#007020">Math</span>.floor(sentiment.magnitude <span style="color:#666">*</span> <span style="color:#40a070">100</span>), <span style="color:#40a070">100</span>) <span style="color:#666">+</span> <span style="color:#4070a0">&#39;%, &#39;</span> <span style="color:#666">+</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#4070a0">&#39;90%) !important&#39;</span>;
</span></span><span style="display:flex;"><span>                <span style="color:#007020;font-weight:bold">return</span> <span style="color:#4070a0">&#39;&lt;span style=&#34;background-color: &#39;</span> <span style="color:#666">+</span> hsl <span style="color:#666">+</span> <span style="color:#4070a0">&#39;&#34;&gt;&#39;</span> <span style="color:#666">+</span> sentiment.sentence <span style="color:#666">+</span> <span style="color:#4070a0">&#39;&lt;/span&gt;&#39;</span>;
</span></span><span style="display:flex;"><span>            }).join(<span style="color:#4070a0">&#39;&amp;nbsp;&amp;nbsp;&#39;</span>);
</span></span><span style="display:flex;"><span>            z(<span style="color:#4070a0">&#39;#para&#39;</span> <span style="color:#666">+</span> paraIdx).html(coloredSentences);
</span></span><span style="display:flex;"><span>        });
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>The first call will fetch the paragraphs from the web scraping service. I display each paragraph right away, uncolored, with an id so that I can then later update each paragraph with colored sentences with their sentiment.</p>
<p>Now for each paragraph, I call the sentences service, which calls the NL API to get the individual sentences of each paragraph. With all the sentences in one go, I use the Promise.all(iterable) method which returns a promise that resolves when all the promises of sentiment analysis per sentence have resolved. This will help me keep track of the order of sentences, as the analysis can give me results in a non predictable order.</p>
<p>I also keep track of the paragraph index to replace all the sentences of each paragraph, once all the promises for the sentences are resolved. I update the paragraph with colored sentences once all sentences of a paragraph are resolved, joining all colored sentences together.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Web scraping and REST API calls on App Engine with Jsoup and groovy-wslite</title><link>https://glaforge.dev/posts/2016/07/27/web-scraping-and-rest-api-calls-on-app-engine-with-jsoup-and-groovy-wslite/</link><pubDate>Wed, 27 Jul 2016 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2016/07/27/web-scraping-and-rest-api-calls-on-app-engine-with-jsoup-and-groovy-wslite/</guid><description>&lt;p>After my &lt;a href="https://glaforge.dev/posts/2016/07/20/sentiment-analysis-on-tweets/">Twitter sentiment article&lt;/a>, those past couple of days, I&amp;rsquo;ve been playing again with the &lt;a href="https://cloud.google.com/natural-language/docs/">Cloud Natural Language API&lt;/a>. This time, I wanted to make a little demo analyzing the text of speeches and remarks published by the press office of the White House. It&amp;rsquo;s interesting to see how speeches alternate negative and positive sequences, to reinforce the argument being exposed.&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-90d80696f1e24c7a793b415cb46126da">
&lt;img src="https://glaforge.dev/img/misc/whitehouse-speeches-630.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-90d80696f1e24c7a793b415cb46126da">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/whitehouse-speeches-630.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>As usual, for my cloud demos, my weapons of choice for rapid development are &lt;a href="http://www.groovy-lang.org/">Apache Groovy&lt;/a>, with &lt;a href="http://glide-gae.appspot.com/">Glide&lt;/a> &amp;amp; &lt;a href="http://gaelyk.appspot.com/">Gaelyk&lt;/a> on &lt;a href="https://cloud.google.com/appengine/">Google App Engine&lt;/a>! But for this demo, I needed two things:&lt;/p></description><content:encoded>
<![CDATA[<p>After my <a href="https://glaforge.dev/posts/2016/07/20/sentiment-analysis-on-tweets/">Twitter sentiment article</a>, those past couple of days, I&rsquo;ve been playing again with the <a href="https://cloud.google.com/natural-language/docs/">Cloud Natural Language API</a>. This time, I wanted to make a little demo analyzing the text of speeches and remarks published by the press office of the White House. It&rsquo;s interesting to see how speeches alternate negative and positive sequences, to reinforce the argument being exposed.</p>
<p><figure>
  <a href="#img-90d80696f1e24c7a793b415cb46126da">
    <img src="/img/misc/whitehouse-speeches-630.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-90d80696f1e24c7a793b415cb46126da">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/whitehouse-speeches-630.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>As usual, for my cloud demos, my weapons of choice for rapid development are <a href="http://www.groovy-lang.org/">Apache Groovy</a>, with <a href="http://glide-gae.appspot.com/">Glide</a> &amp; <a href="http://gaelyk.appspot.com/">Gaelyk</a> on <a href="https://cloud.google.com/appengine/">Google App Engine</a>! But for this demo, I needed two things:</p>
<ul>
<li>a way to scrape the content of the <a href="https://www.whitehouse.gov/briefing-room/speeches-and-remarks">speeches &amp; remarks</a> from the White House press office</li>
<li>a library for easily making REST calls to the <a href="https://cloud.google.com/natural-language/docs/">Natural Language API</a></li>
</ul>
<p>In both cases, we need to issue calls through the internet, and there are some limitations on App Engine with regards to such <a href="https://cloud.google.com/appengine/docs/java/outbound-requests">outbound networking</a>. But if you use the plain Java HTTP / URL networking classes, you are fine. And under the hood, it&rsquo;s using App Engine&rsquo;s own <a href="https://cloud.google.com/appengine/docs/java/issue-requests">URL Fetch service</a>.<br />
I used <a href="https://jsoup.org/">Jsoup</a> for web scraping, which takes care itself for connecting to the web site.<br />
For interacting with the REST API, <a href="https://github.com/jwagenleitner/groovy-wslite">groovy-wslight</a> came to my rescue, although I could have used the Java SDK like in my previous article.<br />
Let&rsquo;s look at Jsoup and scraping first. In my controller fetching the content, I did something along those lines (you can run this script in the Groovy console):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Grab</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;org.jsoup:jsoup:1.9.2&#39;</span><span style="color:#666">)</span> <span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.jsoup.</span><span style="">\</span><span style="color:#666">*</span>   
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> url <span style="color:#666">=</span> <span style="color:#4070a0">&#39;https://www.whitehouse.gov/the-press-office/2016/07/17/statement-president-shootings-baton-rouge-louisiana&#39;</span> <span style="color:#902000">def</span> doc <span style="color:#666">=</span> Jsoup<span style="color:#666">.</span><span style="color:#4070a0">connect</span><span style="color:#666">(</span>url<span style="color:#666">)</span> <span style="color:#666">.</span><span style="color:#4070a0">userAgent</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0&#39;</span><span style="color:#666">)</span> <span style="color:#666">.</span><span style="color:#4070a0">get</span><span style="color:#666">()</span> println doc<span style="color:#666">.</span><span style="color:#4070a0">select</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;.forall-body .field-item p&#39;</span><span style="color:#666">).</span><span style="color:#4070a0">collect</span> <span style="color:#666">{</span> it<span style="color:#666">.</span><span style="color:#4070a0">text</span><span style="color:#666">()</span> <span style="color:#666">}.</span><span style="color:#4070a0">join</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;\\n\\n&#39;</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>Now I&rsquo;m gonna make a call with groovy-wslight to the NL API:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Grab</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;com.github.groovy-wslite:groovy-wslite:1.1.3&#39;</span><span style="color:#666">)</span>  
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">wslite.rest.*</span>   
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> apiKey <span style="color:#666">=</span> <span style="color:#4070a0">&#39;MY_TOP_SECRET_API_KEY&#39;</span>   
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> client <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> RESTClient<span style="color:#666">(</span><span style="color:#4070a0">&#39;https://language.googleapis.com/v1beta1/&#39;</span><span style="color:#666">)</span>  
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> result <span style="color:#666">=</span> client<span style="color:#666">.</span><span style="color:#4070a0">post</span><span style="color:#666">(</span><span style="color:#002070;font-weight:bold">path:</span> <span style="color:#4070a0">&#39;documents:annotateText&#39;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">query:</span> <span style="color:#666">[</span><span style="color:#002070;font-weight:bold">key:</span> apiKey<span style="color:#666">])</span> <span style="color:#666">{</span>  
</span></span><span style="display:flex;"><span>    type ContentType<span style="color:#666">.</span><span style="color:#4070a0">JSON</span>  
</span></span><span style="display:flex;"><span>    json <span style="color:#002070;font-weight:bold">document:</span> <span style="color:#666">[</span>  
</span></span><span style="display:flex;"><span>        type <span style="color:#666">:</span> <span style="color:#4070a0">&#39;PLAIN_TEXT&#39;</span><span style="color:#666">,</span>  
</span></span><span style="display:flex;"><span>        <span style="color:#002070;font-weight:bold">content:</span> text  
</span></span><span style="display:flex;"><span>    <span style="color:#666">],</span> <span style="color:#002070;font-weight:bold">features:</span> <span style="color:#666">[</span>  
</span></span><span style="display:flex;"><span>        extractSyntax <span style="color:#666">:</span> <span style="color:#007020;font-weight:bold">true</span><span style="color:#666">,</span>  
</span></span><span style="display:flex;"><span>        extractEntities <span style="color:#666">:</span> <span style="color:#007020;font-weight:bold">true</span><span style="color:#666">,</span>  
</span></span><span style="display:flex;"><span>        <span style="color:#002070;font-weight:bold">extractDocumentSentiment:</span> <span style="color:#007020;font-weight:bold">true</span>  
</span></span><span style="display:flex;"><span>    <span style="color:#666">]</span>  
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>  
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// returns a list of parsed sentences  
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>println result<span style="color:#666">.</span><span style="color:#4070a0">json</span><span style="color:#666">.</span><span style="color:#4070a0">sentences</span><span style="color:#666">.</span><span style="color:#4070a0">text</span><span style="color:#666">.</span><span style="color:#4070a0">content</span>  
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// prints the overall sentiment of the speech  
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>println result<span style="color:#666">.</span><span style="color:#4070a0">json</span><span style="color:#666">.</span><span style="color:#4070a0">documentSentiment</span><span style="color:#666">.</span><span style="color:#4070a0">polarity</span>
</span></span></code></pre></div><p>Groovy-wslight nicely handles XML and JSON payloads: you can use Groovy maps for the input value, which will be marshalled to JSON transparently, and the GPath notation to easily access the resulting JSON object returned by this API.</p>
<p>It was very quick and straightforward to use Jsoup and groovy-wslight for my web scraping and REST handling needs, and it was a breeze to integrate them in my App Engine application. In a follow-up article, I&rsquo;ll tell you a bit more about the sentiment analysis of the sentences of the speeches, so please stay tuned for the next installment!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Sentiment analysis on tweets</title><link>https://glaforge.dev/posts/2016/07/20/sentiment-analysis-on-tweets/</link><pubDate>Wed, 20 Jul 2016 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2016/07/20/sentiment-analysis-on-tweets/</guid><description>&lt;p>What’s the mood on Twitter today? Looking at my little &lt;a href="https://glaforge.dev/posts/2016/07/11/getting-started-with-glide-and-gaelyk-on-google-app-engine/">twitter demo&lt;/a> from a few weeks ago (using &lt;a href="http://glide-gae.appspot.com/">Glide&lt;/a> &amp;amp; &lt;a href="http://gaelyk.appspot.com/">Gaelyk&lt;/a> on &lt;a href="https://cloud.google.com/appengine/">Google App Engine&lt;/a>), I thought I could enrich the visualization with some sentiment analysis to give more color to those tweets. Fortunately, there’s a new API in Google-town, the &lt;a href="https://cloud.google.com/natural-language/docs/">Cloud Natural Language API&lt;/a> (some more info in the &lt;a href="https://cloudplatform.googleblog.com/2016/07/the-latest-for-Cloud-customers-machine-learning-and-west-coast-expansion.html">announcement&lt;/a> and a great post showing textual &lt;a href="https://cloud.google.com/blog/big-data/2016/07/using-the-cloud-natural-language-api-to-analyze-harry-potter-and-the-new-york-times">analysis of Harry Potter and New York Times&lt;/a>)!&lt;/p>
&lt;p>The brand-new Cloud Natural Language API provides three key services:&lt;/p></description><content:encoded>
<![CDATA[<p>What’s the mood on Twitter today? Looking at my little <a href="https://glaforge.dev/posts/2016/07/11/getting-started-with-glide-and-gaelyk-on-google-app-engine/">twitter demo</a> from a few weeks ago (using <a href="http://glide-gae.appspot.com/">Glide</a> &amp; <a href="http://gaelyk.appspot.com/">Gaelyk</a> on <a href="https://cloud.google.com/appengine/">Google App Engine</a>), I thought I could enrich the visualization with some sentiment analysis to give more color to those tweets. Fortunately, there’s a new API in Google-town, the <a href="https://cloud.google.com/natural-language/docs/">Cloud Natural Language API</a> (some more info in the <a href="https://cloudplatform.googleblog.com/2016/07/the-latest-for-Cloud-customers-machine-learning-and-west-coast-expansion.html">announcement</a> and a great post showing textual <a href="https://cloud.google.com/blog/big-data/2016/07/using-the-cloud-natural-language-api-to-analyze-harry-potter-and-the-new-york-times">analysis of Harry Potter and New York Times</a>)!</p>
<p>The brand-new Cloud Natural Language API provides three key services:</p>
<ul>
<li>
<p><strong>Sentiment analysis</strong>: inspects the given text and identifies the prevailing emotional opinion within the text, especially to determine a writer&rsquo;s attitude as positive, negative, or neutral.</p>
</li>
<li>
<p><strong>Entity recognition</strong>: inspects the given text for known entities (proper nouns such as public figures, landmarks, etc.) and returns information about those entities.</p>
</li>
<li>
<p><strong>Syntax analysis</strong>: extracts linguistic information, breaking up the given text into a series of sentences and tokens (generally, word boundaries), providing further analysis on those tokens.</p>
</li>
</ul>
<p>I’m going to focus only on the sentiment analysis in this article. When analyzing some text, the API tells you whether the content is negative, neutral or positive, returning “polarity” values ranging from -1 for negative to +1 for positive. And you also get a “magnitude”, from 0 to +Infinity to say how strong the emotions expressed are. You can read more about <a href="https://cloud.google.com/natural-language/docs/basics#interpreting_sentiment_analysis_values">what polarity and magnitude mean</a> for a more thorough understanding.</p>
<h2 id="lets-get-started">Let’s get started!</h2>
<p>With the code base of my <a href="https://glaforge.dev/posts/2016/07/11/getting-started-with-glide-and-gaelyk-on-google-app-engine/">first article</a>, I will add the sentiment analysis associated with the tweets I’m fetching. The idea is to come up with a colorful wall of tweets like this, with a range of colors from red for negative, to green for positive, through yellow for neutral:</p>
<p><figure>
  <a href="#img-49801babd5e439ecb5bba92b27d3508a">
    <img src="/img/misc/tweetmood-final-small.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-49801babd5e439ecb5bba92b27d3508a">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/tweetmood-final-small.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I’ll create a new controller (mood.groovy) that will call the Cloud NL service, passing the text as input. I’ll take advantage of App Engine’s Memcache support to cache the calls to the service, as tweets are immutable, their sentiment won’t change. The controller will return a JSON structure to hold the result of the sentiment analysis. From the index.gtpl view template, I’ll add a bit of JavaScript and AJAX to call my newly created controller.</p>
<h2 id="setting-up-the-dependencies">Setting up the dependencies</h2>
<p>You can either use the Cloud NL REST API or the Java SDK. I decided to use the latter, essentially just to benefit from code completion in my IDE. You can have a look at the <a href="https://cloud.google.com/natural-language/docs/samples">Java samples</a> provided. I’m updating the glide.gradle file to define my dependencies, including the google-api-services-language artifact which contains the Cloud NL service. I also needed to depend on the Google API client JARs, and Guava. Here’s what my Gradle dependencies ended up looking like:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>dependencies <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    compile <span style="color:#4070a0">&#34;com.google.api-client:google-api-client:1.21.0&#34;</span>
</span></span><span style="display:flex;"><span>    compile <span style="color:#4070a0">&#34;com.google.api-client:google-api-client-appengine:1.21.0&#34;</span>
</span></span><span style="display:flex;"><span>    compile <span style="color:#4070a0">&#34;com.google.api-client:google-api-client-servlet:1.21.0&#34;</span>
</span></span><span style="display:flex;"><span>    compile <span style="color:#4070a0">&#34;com.google.guava:guava:19.0&#34;</span>
</span></span><span style="display:flex;"><span>    compile <span style="color:#4070a0">&#34;com.google.apis:google-api-services-language:v1beta1-rev1-1.22.0&#34;</span>
</span></span><span style="display:flex;"><span>    compile <span style="color:#4070a0">&#34;org.twitter4j:twitter4j-appengine:4.0.4&#34;</span> <span style="color:#666">}</span>
</span></span></code></pre></div><h2 id="creating-a-new-route-for-the-mood-controller">Creating a new route for the mood controller</h2>
<p>First, let’s create a new route in _routes.groovy to point at the new controller:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>post <span style="color:#4070a0">&#34;/mood&#34;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">forward:</span> <span style="color:#4070a0">&#34;/mood.groovy&#34;</span>
</span></span></code></pre></div><h2 id="coding-the-mood-controller">Coding the mood controller</h2>
<p>Now let’s code the mood.groovy controller!</p>
<p>We’ll need quite a few imports for the Google API client classes, and a couple more for the Cloud Natural Language API:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">com.google.api.client.googleapis.json.GoogleJsonResponseException</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">com.google.api.client.http.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">com.google.api.client.googleapis.auth.oauth2.GoogleCredential</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">com.google.api.client.googleapis.javanet.GoogleNetHttpTransport</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">com.google.api.client.json.jackson2.JacksonFactory</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">com.google.api.services.language.v1beta1.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">com.google.api.services.language.v1beta1.model.*</span>
</span></span></code></pre></div><p>We’re retrieving the text as a parameter, with the params map:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> text <span style="color:#666">=</span> params<span style="color:#666">.</span><span style="color:#4070a0">txt</span>
</span></span></code></pre></div><p>We’ve set up a few local variables that we’ll use for storing and returning the result of the sentiment analysis invocation:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> successOutcome <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">true</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> reason <span style="color:#666">=</span> <span style="color:#4070a0">&#34;&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> polarity <span style="color:#666">=</span> <span style="color:#40a070">0</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> magnitude <span style="color:#666">=</span> <span style="color:#40a070">0</span>
</span></span></code></pre></div><p>Let’s check if we have already got the sentiment analysis for the text parameter in Memcache:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> cachedResult <span style="color:#666">=</span> memcache<span style="color:#666">[</span>text<span style="color:#666">]</span>
</span></span></code></pre></div><p>If it’s in the cache, we’ll be able to return it, otherwise, it’s time to compute it:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(!</span>cachedResult<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">try</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#60a0b0;font-style:italic">// the sentiment analysis calling will be here
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">catch</span> <span style="color:#666">(</span>Throwable t<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        successOutcome <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">false</span>
</span></span><span style="display:flex;"><span>        reason <span style="color:#666">=</span> t<span style="color:#666">.</span><span style="color:#4070a0">message</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>We’re going to wrap our service call with a bit of exception handling, in case something goes wrong, we want to alert the user of what’s going on. And in lieu of the comment, we’ll add some logic to analyze the sentiment We must define the Google credentials allowing us to access the API. Rather than explaining the whole process, please follow the <a href="https://cloud.google.com/natural-language/docs/common/auth">authentication process</a> explained in the documentation to create an API key and a service account:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> credential <span style="color:#666">=</span> GoogleCredential<span style="color:#666">.</span><span style="color:#4070a0">applicationDefault</span><span style="color:#666">.</span><span style="color:#4070a0">createScoped</span><span style="color:#666">(</span>CloudNaturalLanguageAPIScopes<span style="color:#666">.</span><span style="color:#4070a0">all</span><span style="color:#666">())</span>
</span></span></code></pre></div><p>Now we can create our Cloud Natural Language API caller:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> api <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> CloudNaturalLanguageAPI<span style="color:#666">.</span><span style="color:#4070a0">Builder</span><span style="color:#666">(</span>
</span></span><span style="display:flex;"><span>    GoogleNetHttpTransport<span style="color:#666">.</span><span style="color:#4070a0">newTrustedTransport</span><span style="color:#666">(),</span>
</span></span><span style="display:flex;"><span>    JacksonFactory<span style="color:#666">.</span><span style="color:#4070a0">defaultInstance</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">HttpRequestInitializer</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#902000">void</span> <span style="color:#06287e">initialize</span><span style="color:#666">(</span>HttpRequest httpRequest<span style="color:#666">)</span> <span style="color:#007020;font-weight:bold">throws</span> IOException <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>            credential<span style="color:#666">.</span><span style="color:#4070a0">initialize</span><span style="color:#666">(</span>httpRequest<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">})</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">.</span><span style="color:#4070a0">setApplicationName</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;TweetMood&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">.</span><span style="color:#4070a0">build</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>The caller requires some parameters like an HTTP transport, a JSON factory, and a request initializer that double checks that we’re allowed to make those API calls. Now that the API is set up, we can call it:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> sentimentResponse <span style="color:#666">=</span> api<span style="color:#666">.</span><span style="color:#4070a0">documents</span><span style="color:#666">().</span><span style="color:#4070a0">analyzeSentiment</span><span style="color:#666">(</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">AnalyzeSentimentRequest</span><span style="color:#666">(</span><span style="color:#002070;font-weight:bold">document:</span> <span style="color:#007020;font-weight:bold">new</span> Document<span style="color:#666">(</span><span style="color:#002070;font-weight:bold">content:</span> text<span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">type:</span> <span style="color:#4070a0">&#34;PLAIN\_TEXT&#34;</span><span style="color:#666">))</span>
</span></span><span style="display:flex;"><span><span style="color:#666">).</span><span style="color:#4070a0">execute</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>We created an <code>AnalyzeSentimentRequest</code>, passing a <code>Document</code> to analyze with the text of our tweets. Finally, we execute that request. With the values from the response, we’re going to assign our polarity and magnitude variables:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>polarity <span style="color:#666">=</span> sentimentResponse<span style="color:#666">.</span><span style="color:#4070a0">documentSentiment</span><span style="color:#666">.</span><span style="color:#4070a0">polarity</span>
</span></span><span style="display:flex;"><span>magnitude <span style="color:#666">=</span> sentimentResponse<span style="color:#666">.</span><span style="color:#4070a0">documentSentiment</span><span style="color:#666">.</span><span style="color:#4070a0">magnitude</span>
</span></span></code></pre></div><p>Then, we’re going to store the result (successful or not) in Memcache:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>    cachedResult <span style="color:#666">=</span> <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>            <span style="color:#002070;font-weight:bold">success:</span> successOutcome<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>            <span style="color:#002070;font-weight:bold">message:</span> reason<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>            <span style="color:#002070;font-weight:bold">polarity:</span> sentiment<span style="color:#666">?.</span><span style="color:#4070a0">polarity</span> <span style="color:#666">?:</span> <span style="color:#40a070">0.0</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>            <span style="color:#002070;font-weight:bold">magnitude:</span> sentiment<span style="color:#666">?.</span><span style="color:#4070a0">magnitude</span> <span style="color:#666">?:</span> <span style="color:#40a070">0.0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>    memcache<span style="color:#666">[</span>text<span style="color:#666">]</span> <span style="color:#666">=</span> cachedResult
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Now, we setup the JSON content type for the answer, and we can render the cachedResult map as a JSON object with the Groovy JSON builder available inside all controllers:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>response<span style="color:#666">.</span><span style="color:#4070a0">contentType</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#39;application/json&#39;</span>
</span></span><span style="display:flex;"><span>json<span style="color:#666">.</span><span style="color:#4070a0">result</span> cachedResult
</span></span></code></pre></div><h2 id="calling-our-controller-from-the-view">Calling our controller from the view</h2>
<p>A bit of JavaScript &amp; AJAX to the rescue to call the mood controller! I wanted something a bit lighter than jQuery, so I went with <a href="http://zeptojs.com/">Zepto.js</a> for fun. It’s pretty much the same API as jQuery anyway. Just before the end of the body, you can install Zepto from a CDN with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#062873;font-weight:bold">script</span> <span style="color:#4070a0">src</span><span style="color:#666">=</span><span style="color:#4070a0">&#34;https://cdnjs.cloudflare.com/ajax/libs/zepto/1.1.6/zepto.min.js&#34;</span>&gt;&lt;/<span style="color:#062873;font-weight:bold">script</span>&gt;
</span></span></code></pre></div><p>Then, we’ll open up our script tag for some coding:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#062873;font-weight:bold">script</span> <span style="color:#4070a0">language</span><span style="color:#666">=</span><span style="color:#4070a0">&#34;javascript&#34;</span>&gt;
</span></span><span style="display:flex;"><span>Zepto(<span style="color:#007020;font-weight:bold">function</span>(z) {
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// some magic here!
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>});
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#062873;font-weight:bold">script</span>&gt;
</span></span></code></pre></div><p>As the sentiment analysis API call doesn’t support batch requests, we’ll have to call the API for each and every tweet. So let’s iterate over each tweet:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>z(<span style="color:#4070a0">&#39;.tweet&#39;</span>).forEach(<span style="color:#007020;font-weight:bold">function</span>(e, idx) {
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">var</span> txt <span style="color:#666">=</span> z(e).data(<span style="color:#4070a0">&#39;text&#39;</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// ....
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>}
</span></span></code></pre></div><p>Compared to the previous article, I’ve added a data-text attribute to contain the text of the tweet, stripped from hashtags, twitter handles and links (I’ll let you use some regex magic to scratch those bits of text!).</p>
<p>Next, I call my mood controller, passing the trimmed text as input, and check if the response is successful:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span> z.post(<span style="color:#4070a0">&#39;/mood&#39;</span>, { txt<span style="color:#666">:</span> txt }, <span style="color:#007020;font-weight:bold">function</span>(resp) {
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">if</span> (resp.result.success) {
</span></span><span style="display:flex;"><span>        <span style="color:#60a0b0;font-style:italic">// …
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>I retrieve the polarity and magnitude from the JSON payload returned by my mood controller:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span> polarity <span style="color:#666">=</span> resp.result.polarity;
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">var</span> magnitude <span style="color:#666">=</span> resp.result.magnitude;
</span></span></code></pre></div><p>Then I update the background color of my tweets with the following approach. I’m using the HSL color space: Hue, Saturation, Lightness.</p>
<p>The hue ranges from 0 to 360°, and for my tweets, I’m using the first third, from red / 0°, through yellow / 60°, up to green / 120° to represent the polarity, respectively with negative / -1, neutral / 0 and positive / +1.</p>
<p>The saturation (in percents) corresponds to the magnitude. For tweets which are small, the magnitude rarely goes beyond 1, so I simply multiply the magnitude by 100 to get percentages, and floors the results to 100% if it goes beyond.</p>
<p>For the lightness, I’ve got a fixed value of 80%, as 100% would always be full white!</p>
<p>Here’s a more explicit visualization of this color encoding with the following graph:</p>
<p><figure>
  <a href="#img-57d68a72b721b80fbb790923f5b842e1">
    <img src="/img/misc/mood-color-scheme-small.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-57d68a72b721b80fbb790923f5b842e1">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/mood-color-scheme-small.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>So what does the code looks like, with the DOM updates with Zepto?</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span> <span style="color:#007020;font-weight:bold">var</span> hsl <span style="color:#666">=</span> <span style="color:#4070a0">&#39;hsl(&#39;</span> <span style="color:#666">+</span>  <span style="color:#007020">Math</span>.floor((polarity <span style="color:#666">+</span> <span style="color:#40a070">1</span>) <span style="">\</span><span style="color:#666">*</span> <span style="color:#40a070">60</span>) <span style="color:#666">+</span> <span style="color:#4070a0">&#39;, &#39;</span> <span style="color:#666">+</span>  <span style="color:#007020">Math</span>.min(<span style="color:#007020">Math</span>.floor(magnitude <span style="">\</span><span style="color:#666">*</span> <span style="color:#40a070">100</span>), <span style="color:#40a070">100</span>) <span style="color:#666">+</span> <span style="color:#4070a0">&#39;%, &#39;</span> <span style="color:#666">+</span>  <span style="color:#4070a0">&#39;80%) !important&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> z(e).css(<span style="color:#4070a0">&#39;background-color&#39;</span>, hsl)
</span></span><span style="display:flex;"><span>    .data(<span style="color:#4070a0">&#39;polarity&#39;</span>, polarity)
</span></span><span style="display:flex;"><span>    .data(<span style="color:#4070a0">&#39;magnitude&#39;</span>, magnitude);
</span></span></code></pre></div><p>For the fun, I’ve also added some smileys to represent five buckets of positivity / negativity (very negative, negative, neutral, positive, very positive), and from 0 to 3 exclamation marks for 4 buckets of magnitude. That’s what you see in the bottom of the tweet cards in the final screenshot:</p>
<p><figure>
  <a href="#img-12ab00628c6e8259110082da8fe099bf">
    <img src="/img/misc/example-colored-tweet-small.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-12ab00628c6e8259110082da8fe099bf">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/example-colored-tweet-small.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<h2 id="summary">Summary</h2>
<p>But we’re actually done! We have our controller fetching the tweets forwarding to the view template from the last article, and we added a bit of JavaScript &amp; AJAX to call our new mood controller, to display some fancy colors to represent the mood of our tweets, using the brand new <a href="https://cloud.google.com/natural-language/docs/">Cloud Natural Language API</a>.</p>
<p>When playing with sentiment analysis, I was generally on the same opinion regarding sentiment of the tweets, but I was sometimes surprised by the outcome. It’s hard for short bursts of text like tweets to decipher things like irony, sarcasm, etc, and a particular tweet might appear positive when reality it isn’t, and vice versa. Sentiment analysis is probably not an exact science, and you need more context to decide what’s really positive or negative.</p>
<p>Without even speaking of sarcasm or irony, sometimes certain tweets were deemed negative when some particular usually negative words appeared: a “no” or “not” is not necessarily negative when it’s negating something already negative, turning it into something more positive (“it’s not uncool”). For longer text, the general sentiment seems more accurate, so perhaps it’s more appropriate to use sentiment analysis in such cases than on short snippets.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Getting started with Glide and Gaelyk on Google App Engine</title><link>https://glaforge.dev/posts/2016/07/11/getting-started-with-glide-and-gaelyk-on-google-app-engine/</link><pubDate>Mon, 11 Jul 2016 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2016/07/11/getting-started-with-glide-and-gaelyk-on-google-app-engine/</guid><description>&lt;p>Back in 2009, I created &lt;a href="http://gaelyk.appspot.com/">Gaelyk&lt;/a>, a lightweight toolkit for developing &lt;a href="https://cloud.google.com/appengine">Google App Engine&lt;/a>apps using the &lt;a href="http://groovy-lang.org/">Apache Groovy&lt;/a>programming language. I even had the chance to &lt;a href="https://www.youtube.com/watch?v%3DNEnniZTdOYk">speak at Google I/O 2009&lt;/a>about it! Good times, good times… &lt;a href="https://twitter.com/musketyr">Vladimír Oraný&lt;/a>later joined me in maintaining and evolving Gaelyk, and &lt;a href="https://twitter.com/kdabir">Kunal Dabir&lt;/a>created the fun &lt;a href="http://glide-gae.appspot.com/">Glide&lt;/a> project, which is a thin wrapper around Gaelyk to further streamline the development of small to mid-sized apps for Google App Engine.&lt;/p>
&lt;p>Today, I want to share with you a quick start guide to develop a little app, that shows some tweets from selected accounts with the &lt;a href="https://dev.twitter.com/rest/public">Twitter API&lt;/a>(thanks to &lt;a href="http://twitter4j.org/en/">Twitter4J&lt;/a>), and using the &lt;a href="https://getmdl.io/">Material Design Light&lt;/a> template for the look’n feel (I used the “&lt;a href="https://getmdl.io/templates/dashboard/index.html">dashboard&lt;/a>” template). I won’t list all the exact steps, all the precise changes made to the templates, etc, but I want to give you the keys for having a productive experience with Glide and Gaelyk on App Engine. And here’s a screenshot of what we’ll be building:&lt;/p></description><content:encoded>
<![CDATA[<p>Back in 2009, I created <a href="http://gaelyk.appspot.com/">Gaelyk</a>, a lightweight toolkit for developing <a href="https://cloud.google.com/appengine">Google App Engine</a>apps using the <a href="http://groovy-lang.org/">Apache Groovy</a>programming language. I even had the chance to <a href="https://www.youtube.com/watch?v%3DNEnniZTdOYk">speak at Google I/O 2009</a>about it! Good times, good times… <a href="https://twitter.com/musketyr">Vladimír Oraný</a>later joined me in maintaining and evolving Gaelyk, and <a href="https://twitter.com/kdabir">Kunal Dabir</a>created the fun <a href="http://glide-gae.appspot.com/">Glide</a> project, which is a thin wrapper around Gaelyk to further streamline the development of small to mid-sized apps for Google App Engine.</p>
<p>Today, I want to share with you a quick start guide to develop a little app, that shows some tweets from selected accounts with the <a href="https://dev.twitter.com/rest/public">Twitter API</a>(thanks to <a href="http://twitter4j.org/en/">Twitter4J</a>), and using the <a href="https://getmdl.io/">Material Design Light</a> template for the look’n feel (I used the “<a href="https://getmdl.io/templates/dashboard/index.html">dashboard</a>” template). I won’t list all the exact steps, all the precise changes made to the templates, etc, but I want to give you the keys for having a productive experience with Glide and Gaelyk on App Engine. And here’s a screenshot of what we’ll be building:</p>
<p><figure>
  <a href="#img-8276ef0a9d985887a00406341f4f3c0e">
    <img src="/img/misc/tweet-glide.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-8276ef0a9d985887a00406341f4f3c0e">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/tweet-glide.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Ready? Let’s start!</p>
<h2 id="installing-glide">Installing Glide</h2>
<p>In the Groovy community, most developers these days are using <a href="http://sdkman.io/">SDKMan</a> to install SDKs for Groovy, Gradle, Grails, and more. Glide also comes in the form of an SDK, with a command-line, and is available via SDKMan. So, first step, let’s install SDKMan from your shell (there’s also a Windows-friendly version):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ curl -s <span style="color:#4070a0">&#34;https://get.sdkman.io&#34;</span> | bash
</span></span></code></pre></div><p>It will automatically install the SDK manager. Then, either you just open up a new terminal, or you run the following command, to have access to SDKMan in your current session:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ <span style="color:#007020">source</span> <span style="color:#4070a0">&#34;</span><span style="color:#bb60d5">$HOME</span><span style="color:#4070a0">/.sdkman/bin/sdkman-init.sh&#34;</span>
</span></span></code></pre></div><p>To check the installation succeeded, you can run it with the sdk command, for example by printing the current version of SDKMan:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ sdk version
</span></span></code></pre></div><p>Now that SDKMan is installed, it’s time to install Glide as well:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ sdk install glide
</span></span></code></pre></div><p>You can then check that glide is indeed functioning correctly by executing:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ glide
</span></span></code></pre></div><p>If you’re on Windows or if you’re not planning to keep SDKMan around, you can also <a href="http://glide-gae.appspot.com/docs/installing">install Glide by other means</a>, manually, as explained in the documentation.</p>
<h2 id="creating-the-skeleton-of-our-application">Creating the skeleton of our application</h2>
<p>Okay, we’re ready to create our first Glide / Gaelyk application! (You can also check out the Glide <a href="http://glide-gae.appspot.com/docs/quick-start">tutorial</a> as well)</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ glide --app tweetapp create 
</span></span><span style="display:flex;"><span>$ <span style="color:#007020">cd</span> tweetapp 
</span></span><span style="display:flex;"><span>$ glide run
</span></span></code></pre></div><p>Head over to your browser at http://localhost:8080/, and you’ll see a brilliant “hello glide” message showing up. So far so good, the app is running locally, transparently thanks to the App Engine SDK, now let’s tweak this skeleton!</p>
<p>The project structure is pretty simple, in the directory, you’ll see a glide.groovy file at the root, and an “app” sub-folder containing index.groovy and <code>_routes.groovy</code>:</p>
<ul>
<li><code>glide.groovy</code> — the configuration file for your app</li>
<li><code>index.groovy</code> — the default controller</li>
<li><code>_routes.groovy</code> — listing the mappings between URLs and controllers, and more</li>
</ul>
<h2 id="configuring-our-application">Configuring our application</h2>
<p>In <code>glide.groovy</code>, you’ll have the app name and version name defined:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>app <span style="color:#666">{</span> 
</span></span><span style="display:flex;"><span>    name<span style="color:#666">=</span><span style="color:#4070a0">&#34;my-tweet-demo&#34;</span> 
</span></span><span style="display:flex;"><span>    version<span style="color:#666">=</span><span style="color:#4070a0">&#34;1&#34;</span> 
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>You might have to change the application name, as we shall see later on, when we deploy the application.</p>
<p>To use the latest version of the App Engine SDK, you can append the following to explicitly ask for a specific version of the SDK:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>glide <span style="color:#666">{</span> 
</span></span><span style="display:flex;"><span>    versions <span style="color:#666">{</span> 
</span></span><span style="display:flex;"><span>        appengineVersion <span style="color:#666">=</span> <span style="color:#4070a0">&#34;1.9.38&#34;</span> 
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span> 
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><h2 id="defining-library-dependencies">Defining library dependencies</h2>
<p>At the root of our project, we’ll actually add a new configuration file: glide.gradle. This file will allow us to define library dependencies. It’s basically a fragment of a <a href="https://gradle.org/">Gradle</a>build configuration, where you can define those dependencies using the usual Gradle <a href="https://docs.gradle.org/current/userguide/artifact_dependencies_tutorial.html">syntax</a>. In our glide.gradle file, we’ll add the following dependency, for our Twitter integration:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>dependencies <span style="color:#666">{</span> 
</span></span><span style="display:flex;"><span>    compile <span style="color:#4070a0">&#34;org.twitter4j:twitter4j-appengine:4.0.4&#34;</span> 
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><h2 id="using-the-material-design-lite-template">Using the Material Design Lite template</h2>
<p>To make things pretty, we’ll be using the <a href="https://getmdl.io/templates/index.html">Material Design Lite dashboard sample</a>, but feel free to skip this part if you want to go straight to the coding part! Download the ZIP archive. It comes with an index.html file, as well as a style.css stylesheet. We’ll copy both files to the app/ folder, but we’ll rename index.html into index.gtpl (to make it a Groovy template file).</p>
<p>When you have a bigger project, with more assets, it’s obviously better to organize these views, stylesheets, JavaScript files, images, etc, in their own respective sub-folders. But for the purpose of my demo, I’ll keep everything in the same place.</p>
<p>You’ll see the template installed and visible if you go to this local URL:</p>
<p><a href="http://localhost:8080/index.gtpl">http://localhost:8080/index.gtpl</a></p>
<p>I won’t detail all the changes to make to the template, and I’ll let you clean the template yourselves, but we can already remove everything that’s inside the inner div of the main tag: that’s where we’ll display our tweets!</p>
<h2 id="lets-make-pretty-urls">Let&rsquo;s make pretty URLs!</h2>
<p>We’d like to have some nice URLs for our app. For that, we’ll now have a look at the _routes.groovy file where you can define your URL mappings, to point at templates (*.gtpl files) or at controllers (*.groovy files, that can render some output directly or forward to templates for rich views). What shall we put in our routes definitions?</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>get <span style="color:#4070a0">&#34;/&#34;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">redirect:</span> <span style="color:#4070a0">&#34;/u/glaforge&#34;</span>  
</span></span><span style="display:flex;"><span>get <span style="color:#4070a0">&#34;/u/@who&#34;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">forward:</span> <span style="color:#4070a0">&#34;/index.groovy?u=@who&#34;</span><span style="color:#666">,</span> 
</span></span><span style="display:flex;"><span>    <span style="color:#002070;font-weight:bold">validate:</span> <span style="color:#666">{</span> request<span style="color:#666">.</span><span style="color:#4070a0">who</span> <span style="color:#666">==~</span> <span style="color:#4070a0">/[a-zA-Z0-9_]{1,15}/</span> <span style="color:#666">},</span>  
</span></span><span style="display:flex;"><span>    <span style="color:#002070;font-weight:bold">cache:</span> <span style="color:#40a070">1</span><span style="color:#666">.</span><span style="color:#4070a0">minute</span>  
</span></span><span style="display:flex;"><span>get <span style="color:#4070a0">&#34;/u/@who&#34;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">forward:</span> <span style="color:#4070a0">&#34;/index.groovy?u=@who&amp;error=invalid&#34;</span>
</span></span></code></pre></div><p>You can have a look at the Gaelyk documentation that defines the <a href="http://gaelyk.appspot.com/tutorial/url-routing%23route-definition">routes definition syntax</a> for further explanations on what’s possible.</p>
<p>The root of the app, <code>/</code>, will redirect to /u/glaforge, to visualize my latest tweets. And all URLs like <code>/u/*</code> will forward to our index.groovy controller, that will fetch the tweets for that Twitter user, and forward them to the index.gtpl view for rendering the result.</p>
<pre tabindex="0"><code>/u/glaforge → /index.groovy?u=glaforge → /index.gtpl
</code></pre><p>The routing syntax is using the <code>@foo</code> notation to denote query path variables, that we can then reuse in the forwarding part.</p>
<p>The routing rules are evaluated in order, and the first one that matches the URL will be chosen. We have two get <code>/u/@who</code> routes, in the first case, we have a validation rule that checks that the @who path variable is a valid Twitter handle (using Groovy’s <a href="http://docs.groovy-lang.org/latest/html/documentation/index.html%23_regular_expression_operators">regular expression matching</a> operator). If the validation fails, this route isn’t chosen, and the chain will continue, and it will fall back to the following route that forwards to the template with an error query parameter.</p>
<p>Also interesting to note is the use of caching, with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#002070;font-weight:bold">cache:</span> <span style="color:#40a070">1</span><span style="color:#666">.</span><span style="color:#4070a0">minute</span>
</span></span></code></pre></div><p>The output of this URL will be put in App Engine’s Memcache so that for the next minute, all requests to the same URL will be fetched from the cache, rather than having to call again the controller and the Twitter API, thus saving on computation and on third-party API call quota.</p>
<p>For the purpose of development, you might want to comment that caching configuration, as you do want to see changes to that template or controller as you’re making changes.</p>
<h2 id="time-to-code-our-tweet-fetching-controller">Time to code our tweet fetching controller</h2>
<p>To user the Twitter API, you’ll have to register a new application on the <a href="https://apps.twitter.com/">Twitter Apps page</a>. Twitter will give you the right credentials that you’ll need to connect to the API. You’ll need the four following keys to configure the Twitter4J library:</p>
<ul>
<li>the consumer API key</li>
<li>the secrete consumer API key</li>
<li>the access token</li>
<li>and the secret access token</li>
</ul>
<p>Let’s configure Twitter4J with that information. I’ll implement the “happy path” and will skip part of the proper error handling (an exercise for the reader?), to keep the code short for this article.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">twitter4j.*</span> 
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">twitter4j.conf.*</span>   
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> conf <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> ConfigurationBuilder<span style="color:#666">(</span>
</span></span><span style="display:flex;"><span>    <span style="color:#002070;font-weight:bold">debugEnabled:</span> <span style="color:#007020;font-weight:bold">true</span><span style="color:#666">,</span> 
</span></span><span style="display:flex;"><span>    <span style="color:#002070;font-weight:bold">OAuthAccessToken:</span> <span style="color:#4070a0">&#34;CHANGE_ME&#34;</span><span style="color:#666">,</span> 
</span></span><span style="display:flex;"><span>    <span style="color:#002070;font-weight:bold">OAuthAccessTokenSecret:</span> <span style="color:#4070a0">&#34;CHANGE_ME&#34;</span><span style="color:#666">,</span> 
</span></span><span style="display:flex;"><span>    <span style="color:#002070;font-weight:bold">OAuthConsumerKey:</span> <span style="color:#4070a0">&#34;CHANGE_ME&#34;</span><span style="color:#666">,</span> 
</span></span><span style="display:flex;"><span>    <span style="color:#002070;font-weight:bold">OAuthConsumerSecret:</span> <span style="color:#4070a0">&#34;CHANGE_ME&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#666">.</span><span style="color:#4070a0">build</span><span style="color:#666">()</span>   
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> twitter <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> TwitterFactory<span style="color:#666">(</span>conf<span style="color:#666">).</span><span style="color:#4070a0">instance</span>
</span></span></code></pre></div><p>The API is configured with your credentials. Be sure to replace all the <code>CHANGE_ME</code> bits, obviously!</p>
<p>Let’s lookup the Twitter handle coming through the query parameter, thanks to the user ‘u’ attribute on the params map:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> accounts <span style="color:#666">=</span> twitter<span style="color:#666">.</span><span style="color:#4070a0">lookupUsers</span><span style="color:#666">(</span>params<span style="color:#666">.</span><span style="color:#4070a0">u</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>There should only be two cases (that’s where there may be some more error handling to do!): 1) there’s no user found, or there’s only one. Let’s start with no user found:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>accounts<span style="color:#666">.</span><span style="color:#4070a0">isEmpty</span><span style="color:#666">())</span> <span style="color:#666">{</span> 
</span></span><span style="display:flex;"><span>    request<span style="color:#666">.</span><span style="color:#4070a0">errorMessage</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Account &#39;${params.u}&#39; doesn&#39;t exist.&#34;</span> 
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>If no user account was found, we’ll put an error message in the request that’ll be forwarded to our view template.</p>
<p>In the else branch, we’ll handle the the normal case where the user was found:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">else</span> <span style="color:#666">{</span> 
</span></span><span style="display:flex;"><span>    User userAccount <span style="color:#666">=</span> accounts<span style="color:#666">[</span><span style="color:#40a070">0</span><span style="color:#666">]</span> 
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> tweets <span style="color:#666">=</span> twitter<span style="color:#666">.</span><span style="color:#4070a0">search</span><span style="color:#666">(</span><span style="color:#007020;font-weight:bold">new</span> Query<span style="color:#666">(</span><span style="color:#4070a0">&#34;from:${params.u}&#34;</span><span style="color:#666">))</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">.</span><span style="color:#4070a0">tweets</span><span style="color:#666">.</span><span style="color:#4070a0">findAll</span> <span style="color:#666">{</span> <span style="color:#666">!</span>it<span style="color:#666">.</span><span style="color:#4070a0">isRetweet</span><span style="color:#666">()</span> <span style="color:#666">}</span>
</span></span></code></pre></div><p>We get the first account returned, and issue a search request for the latest tweets from that account. We filter out the retweets to keep only the user’s original tweets (but it’s up to you if you want to keep them).</p>
<p>In the request for the view, we’ll add details about the account:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>request<span style="color:#666">.</span><span style="color:#4070a0">account</span> <span style="color:#666">=</span> <span style="color:#666">[</span> 
</span></span><span style="display:flex;"><span>    name <span style="color:#666">:</span> userAccount<span style="color:#666">.</span><span style="color:#4070a0">name</span><span style="color:#666">,</span> 
</span></span><span style="display:flex;"><span>    <span style="color:#002070;font-weight:bold">handle:</span> userAccount<span style="color:#666">.</span><span style="color:#4070a0">screenName</span><span style="color:#666">,</span> 
</span></span><span style="display:flex;"><span>    <span style="color:#002070;font-weight:bold">avatar:</span> userAccount<span style="color:#666">.</span><span style="color:#4070a0">biggerProfileImageURL</span>  
</span></span><span style="display:flex;"><span><span style="color:#666">]</span>
</span></span></code></pre></div><p>And we’ll also add the list of tweets:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>request<span style="color:#666">.</span><span style="color:#4070a0">tweets</span> <span style="color:#666">=</span> tweets<span style="color:#666">.</span><span style="color:#4070a0">collect</span> <span style="color:#666">{</span> Status s <span style="color:#666">-&gt;</span> <span style="color:#666">[</span>  
</span></span><span style="display:flex;"><span>        id <span style="color:#666">:</span> s<span style="color:#666">.</span><span style="color:#4070a0">id</span><span style="color:#666">,</span> 
</span></span><span style="display:flex;"><span>        <span style="color:#002070;font-weight:bold">timestamp:</span> s<span style="color:#666">.</span><span style="color:#4070a0">createdAt</span><span style="color:#666">.</span><span style="color:#4070a0">time</span><span style="color:#666">,</span>  
</span></span><span style="display:flex;"><span>        content <span style="color:#666">:</span> s<span style="color:#666">.</span><span style="color:#4070a0">text</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">]</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>And to finish our controller, we’ll forward to the view:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>forward <span style="color:#4070a0">&#39;index.gtpl&#39;</span>
</span></span></code></pre></div><p>Now that our controller is ready, we’ll have to surface the data into the view template.</p>
<h2 id="modify-the-view-template">Modify the view template</h2>
<p>Wherever the template displays the “Home” label, we’ll replace these with the Twitter handle. For that, we can use String interpolation in the template with the ${} notation. If there’s no error message, there should be an account, and we display that handle.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>$<span style="color:#666">{</span> request<span style="color:#666">.</span><span style="color:#4070a0">errorMessage</span> <span style="color:#666">?</span> <span style="color:#4070a0">&#39;Home&#39;</span> <span style="color:#666">:</span> <span style="color:#4070a0">&#39;@&#39;</span> <span style="color:#666">+</span>request<span style="color:#666">.</span><span style="color:#4070a0">account</span><span style="color:#666">.</span><span style="color:#4070a0">handle</span> <span style="color:#666">}</span>
</span></span></code></pre></div><p>Let’s display the list of tweets, or the error message if there’s one. We’ll iterate over the tweets from the request attributes, and add the following in the inner div of the main tag (fore brevity sake, I&rsquo;ll remove the divs and css needed to make things pretty):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#666">&lt;%</span> 
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>request<span style="color:#666">.</span><span style="color:#4070a0">tweets</span><span style="color:#666">)</span> <span style="color:#666">{</span> 
</span></span><span style="display:flex;"><span>    request<span style="color:#666">.</span><span style="color:#4070a0">tweets</span><span style="color:#666">.</span><span style="color:#4070a0">each</span> <span style="color:#666">{</span> tweet <span style="color:#666">-&gt;</span> <span style="color:#666">%&gt;</span> $<span style="color:#666">{</span>tweet<span style="color:#666">.</span><span style="color:#4070a0">content</span><span style="color:#666">}</span> <span style="color:#666">&lt;%</span> <span style="color:#666">}</span> 
</span></span><span style="display:flex;"><span><span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">else</span> <span style="color:#666">{</span> <span style="color:#666">%&gt;</span> 
</span></span><span style="display:flex;"><span>    $<span style="color:#666">{</span>request<span style="color:#666">.</span><span style="color:#4070a0">errorMessage</span><span style="color:#666">}</span>  
</span></span><span style="display:flex;"><span><span style="color:#666">&lt;%</span> 
</span></span><span style="display:flex;"><span><span style="color:#666">}</span> <span style="color:#666">%&gt;</span>
</span></span></code></pre></div><p>And voila, our app is ready! Well, at least, it works locally on our app server, but it’s time to deploy it for real on App Engine!</p>
<h2 id="deploying-to-google-app-engine">Deploying to Google App Engine</h2>
<p>Let’s login in the <a href="https://console.cloud.google.com">Google Cloud Platform console</a>to create our application project. If you don’t already have an account, you can benefit from the <a href="https://cloud.google.com/free-trial/">free trial which offers $300 of credits</a> for the full platform.</p>
<p><figure>
  <a href="#img-aabce51e7d3d6277c5b3f76237b48433">
    <img src="/img/src/app-engine-project-creation.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-aabce51e7d3d6277c5b3f76237b48433">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/src/app-engine-project-creation.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Be sure to pay attention to the actual project ID that will have been created, it may be slightly different than the project name itself. This project ID is also called the app ID, and that’s the actually what you have to put in the glide.groovy file, in the app.name field (right, it’s a bit confusing, isn’t it?)</p>
<p>When the project is created, you’re able to use the glide command-line to deploy the application:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ glide upload
</span></span></code></pre></div><p>If you see an error like below in the logs, it might mean that there’s a problem with your app ID, so be sure to double check it’s correct:</p>
<blockquote>
<p>403 Forbidden You do not have permission to modify this app (app_id=u&rsquo;s~my-tweet-demo&rsquo;).</p></blockquote>
<p>Another occurrence of this error message is when you are using different accounts with Google Cloud Platform. For instance, in my case, I have both a personal gmail account for my personal apps, and a google.com account for my work related apps. I had to zap <code>~/.appcfg_oauth2_tokens_java</code> to let the upload logic to use the correct account, and ask me to authentication with OAuth2.</p>
<p>Once the upload succeeded, you can access your app here:</p>
<p><a href="http://my-tweet-demo.appspot.com">http://my-tweet-demo.appspot.com</a></p>
<p>Hooray, you’ve done it! :-)</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>What can we learn from million lines of Groovy code on Github?</title><link>https://glaforge.dev/posts/2016/07/06/what-can-we-learn-from-million-lines-of-groovy-code-on-github/</link><pubDate>Wed, 06 Jul 2016 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2016/07/06/what-can-we-learn-from-million-lines-of-groovy-code-on-github/</guid><description>&lt;p>Github and Google recently announced and released the Github archive to BigQuery, liberating a huge dataset of source code in multiple programming languages, and making it easier to query it and discover some insights.&lt;/p>
&lt;p>Github explained that &lt;a href="https://github.com/blog/2201-making-open-source-data-more-available">the dataset comprises over 3 terabytes&lt;/a> of data, for 2.8 million repositories, 145 million commits over 2 billion file paths! The &lt;a href="https://cloudplatform.googleblog.com">Google Cloud Platform blog&lt;/a> gave some &lt;a href="https://cloudplatform.googleblog.com/2016/06/GitHub-on-BigQuery-analyze-all-the-open-source-code.html">additional pointers&lt;/a> to give hints about what’s possible to do with the querying capabilities of BigQuery. Also, you can have a look at the &lt;a href="https://cloud.google.com/bigquery/public-data/github">getting started guide&lt;/a> with the steps to follow to have fun yourself with the dataset.&lt;/p></description><content:encoded>
<![CDATA[<p>Github and Google recently announced and released the Github archive to BigQuery, liberating a huge dataset of source code in multiple programming languages, and making it easier to query it and discover some insights.</p>
<p>Github explained that <a href="https://github.com/blog/2201-making-open-source-data-more-available">the dataset comprises over 3 terabytes</a> of data, for 2.8 million repositories, 145 million commits over 2 billion file paths! The <a href="https://cloudplatform.googleblog.com">Google Cloud Platform blog</a> gave some <a href="https://cloudplatform.googleblog.com/2016/06/GitHub-on-BigQuery-analyze-all-the-open-source-code.html">additional pointers</a> to give hints about what’s possible to do with the querying capabilities of BigQuery. Also, you can have a look at the <a href="https://cloud.google.com/bigquery/public-data/github">getting started guide</a> with the steps to follow to have fun yourself with the dataset.</p>
<p>My colleagues Felipe gave some <a href="https://medium.com/@hoffa/github-on-bigquery-analyze-all-the-code-b3576fd2b150#.hxdz073uu">interesting stats</a> about the top programming languages, or licenses, while Francesc did some interesting <a href="https://medium.com/google-cloud/analyzing-go-code-with-bigquery-485c70c3b451#.7qw4hmvly">analysis of Go repositories</a>. So I was curious to investigate myself this dataset to run some queries about the <a href="http://groovy-lang.org/">Groovy programming language</a>!</p>
<p>Without further ado, let’s dive in!</p>
<p>If you don’t already have an account of the Google Cloud Platform, you’ll be able to get the <a href="https://cloud.google.com/free-trial">free trial</a>, with $300 of credits to discover and have fun with all the products and services of the platform. Then, be sure to have a look at the <a href="https://cloud.google.com/bigquery/public-data/github#github">Github dataset getting started guide</a> I’ve mentioned above which can give you some ideas of things to try out, and the relevant steps to start tinkering with the data.</p>
<p>In the Google Cloud Platform <a href="https://console.cloud.google.com/">console</a>, I’ve created an empty project (for me, called “github-groovy-files”) that will host my project and the subset of the whole dataset to focus on the Groovy source files only.</p>
<p>Next, we can go to the Github public dataset on BigQuery:<br />
<a href="https://bigquery.cloud.google.com/dataset/bigquery-public-data:github_repos">https://bigquery.cloud.google.com/dataset/bigquery-public-data:github_repos</a></p>
<p>I created a new dataset called “github”, whose location is in the US (the default). Be sure to keep the default location in the US as the Github dataset is in that region already.</p>
<p><figure>
  <a href="#img-d93e390b9401f359b9b6d5621e57d79e">
    <img src="/img/bq-groovy/create-new-dataset-1.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-d93e390b9401f359b9b6d5621e57d79e">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bq-groovy/create-new-dataset-1.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p><figure>
  <a href="#img-08da482b415b1707724cdfa9e1b4e0bb">
    <img src="/img/bq-groovy/create-new-dataset-2.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-08da482b415b1707724cdfa9e1b4e0bb">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bq-groovy/create-new-dataset-2.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I launched the following query to list all the Groovy source files, and save them in a new table called “files” for further querying:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span><span style="color:#666">*</span><span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[bigquery<span style="color:#666">-</span><span style="color:#007020;font-weight:bold">public</span><span style="color:#666">-</span><span style="color:#007020;font-weight:bold">data</span>:github_repos.files]<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">WHERE</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">RIGHT</span>(path,<span style="color:#bbb"> </span><span style="color:#40a070">7</span>)<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#39;.groovy&#39;</span><span style="color:#bbb"> 
</span></span></span></code></pre></div><p>Now that I have my own subset of the dataset with only the Groovy files, I ran a count query to know the number of Groovy files available:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">COUNT</span>(<span style="color:#666">*</span>)<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[github<span style="color:#666">-</span>groovy<span style="color:#666">-</span>files:github.files]<span style="color:#bbb"> 
</span></span></span></code></pre></div><p>There are 743 070 of Groovy source files!</p>
<p>I was curious to see if there were some common names of Groovy scripts and classes that would appear more often than others:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>TOP(filename,<span style="color:#bbb"> </span><span style="color:#40a070">24</span>),<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">COUNT</span>(<span style="">\</span><span style="color:#666">*</span>)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">as</span><span style="color:#bbb"> </span>n<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">LAST</span>(SPLIT(path,<span style="color:#bbb"> </span><span style="color:#4070a0">&#39;/&#39;</span>))<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">as</span><span style="color:#bbb"> </span>filename<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[github.files]<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb"> 
</span></span></span></code></pre></div><p><figure>
  <a href="#img-e15bcb10e94a0132a06b9ede95fd55ce">
    <img src="/img/bq-groovy/frequent-name-1.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-e15bcb10e94a0132a06b9ede95fd55ce">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bq-groovy/frequent-name-1.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
 <figure>
  <a href="#img-d3371590fc47e4a91fd7fd192f994508">
    <img src="/img/bq-groovy/frequent-name-2.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-d3371590fc47e4a91fd7fd192f994508">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bq-groovy/frequent-name-2.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I was surprised to see A.groovy being the most frequent file name! I haven’t dived deeper yet, but I’d be curious to see what’s in those A.groovy files, as well as B.groovy or a.groovy in 4th and 13th positions respectively.</p>
<p><a href="http://groovy-lang.org/">Apache Groovy</a> is often used for various automation tasks, and I’ve found many Maven or Jenkins scripts to check that a certain task or job terminated correctly thanks to scripts called verify.groovy.</p>
<p>Files like <code>BuildConfig.groovy</code>, <code>Config.groovy</code>, <code>UrlMappings.groovy</code>, <code>DataSource.groovy</code>, <code>BootStrap.groovy</code> clearly come from the usual files found in <a href="https://grails.org/">Grails framework</a> web applications.</p>
<p>You can also see configuration files like <code>logback.groovy</code> to configure the Logback logging library.</p>
<p>You don’t see usage of the <a href="https://gradle.org/">Gradle</a> build automation tool here, because I only selected files with a .groovy extension, and not files with the .gradle extension. But we’ll come back to Gradle in a moment.</p>
<p>So far, we’ve looked at the file names only, not at their content. That’s where we need another table, coming from the “contents” table of the dataset, that we’ll filter thanks to the file names we’ve saved in our “files” table, thanks to this query:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span><span style="color:#666">*</span><span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[bigquery<span style="color:#666">-</span><span style="color:#007020;font-weight:bold">public</span><span style="color:#666">-</span><span style="color:#007020;font-weight:bold">data</span>:github_repos.contents]<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">WHERE</span><span style="color:#bbb"> </span>id<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">IN</span><span style="color:#bbb"> </span>(<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>id<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[github.files]<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb">
</span></span></span></code></pre></div><p>As this is a lot of content, I had to save the result of the query in a new table called “contents”, and I had to check the box “allow large results” in the options pane that you can open thanks to the “Show options” button below the query editor.</p>
<p>From the 743 070 files, how many lines of Groovy code do you think there are in them? For that purpose, we need to split the raw content of the files per lines, as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">COUNT</span>(line)<span style="color:#bbb"> </span>total_lines<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>SPLIT(content,<span style="color:#bbb"> </span><span style="color:#4070a0">&#39;\n&#39;</span>)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">AS</span><span style="color:#bbb"> </span>line<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[github<span style="color:#666">-</span>groovy<span style="color:#666">-</span>files:github.contents]<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb"> 
</span></span></span></code></pre></div><p>We have 16,464,376 lines of code over the our 743,070 Groovy files. That’s an average of 22 lines per file, which is pretty low! It would be more interesting to draw some histogram to see the distribution of those lines of code. We can use <a href="https://en.wikipedia.org/wiki/Quantile">quantiles</a> to have a better idea of the distribution with this query with 10 quantiles:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>QUANTILES(total_lines,<span style="color:#bbb"> </span><span style="color:#40a070">10</span>)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">AS</span><span style="color:#bbb"> </span>q<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">COUNT</span>(line)<span style="color:#bbb"> </span>total_lines<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>SPLIT(content,<span style="color:#bbb"> </span><span style="color:#4070a0">&#39;\n&#39;</span>)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">AS</span><span style="color:#bbb"> </span>line,<span style="color:#bbb"> </span>id<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[github<span style="color:#666">-</span>groovy<span style="color:#666">-</span>files:github.contents]<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">GROUP</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">BY</span><span style="color:#bbb"> </span>id<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb"> 
</span></span></span></code></pre></div><p>Which gives this resulting table:</p>
<p><figure>
  <a href="#img-bee31c82325b83716df6403d5372e7ba">
    <img src="/img/bq-groovy/quantiles-lines.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-bee31c82325b83716df6403d5372e7ba">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bq-groovy/quantiles-lines.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>There are files with 0 lines of code! And the biggest one is 9506 lines long! 10% are 11 lines long or less, half are 37 lines or less, etc. And 10% are longer than 149 lines.</p>
<p>Let’s now have a look at packages and imports for a change.</p>
<p>Do you know what are the most frequent packages used?</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>package,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">COUNT</span>(<span style="color:#666">*</span>)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span><span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>REGEXP_EXTRACT(line,<span style="color:#bbb"> </span>r<span style="color:#4070a0">&#39; (\[a-z0-9\\.\_\]\*)\\.&#39;</span>)<span style="color:#bbb"> </span>package,<span style="color:#bbb"> </span>id<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>SPLIT(content,<span style="color:#bbb"> </span><span style="color:#4070a0">&#39;\n&#39;</span>)<span style="color:#bbb"> </span>line,<span style="color:#bbb"> </span>id<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[github<span style="color:#666">-</span>groovy<span style="color:#666">-</span>files:github.contents]<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">WHERE</span><span style="color:#bbb"> </span>content<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">CONTAINS</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#39;import&#39;</span><span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">HAVING</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">LEFT</span>(line,<span style="color:#bbb"> </span><span style="color:#40a070">6</span>)<span style="color:#666">=</span><span style="color:#4070a0">&#39;import&#39;</span><span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">GROUP</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">BY</span><span style="color:#bbb"> </span>package,<span style="color:#bbb"> </span>id<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">GROUP</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">BY</span><span style="color:#bbb"> </span><span style="color:#40a070">1</span><span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">ORDER</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">BY</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">DESC</span><span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">LIMIT</span><span style="color:#bbb"> </span><span style="color:#40a070">30</span>;<span style="color:#bbb">
</span></span></span></code></pre></div><p><figure>
  <a href="#img-a443e40e4ab25b8ed869b9f6270388a6">
    <img src="/img/bq-groovy/package-name-1.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-a443e40e4ab25b8ed869b9f6270388a6">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bq-groovy/package-name-1.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
 <figure>
  <a href="#img-b400568385d30ecf627edb82b00c0c07">
    <img src="/img/bq-groovy/package-name-2.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-b400568385d30ecf627edb82b00c0c07">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bq-groovy/package-name-2.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
 <figure>
  <a href="#img-3ed131ef97b21af0fa9a854cd67144f4">
    <img src="/img/bq-groovy/package-name-3.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-3ed131ef97b21af0fa9a854cd67144f4">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bq-groovy/package-name-3.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>The <a href="http://docs.spockframework.org/">Spock</a> and JUnit testing frameworks are the most widely used packages, showing that Groovy is used a lot for testing! We also see a lot of <a href="https://grails.org/">Grails</a> and <a href="https://gradle.org/">Gradle</a> related packages, and some logging, some Spring, Joda-Time, Java util-concurrent or servlets, etc.</p>
<p>We can zoom in the <code>groovy.*</code> packages with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>package,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">COUNT</span>(<span style="color:#666">*</span>)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span><span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>REGEXP_EXTRACT(line,<span style="color:#bbb"> </span>r<span style="color:#4070a0">&#39; ([a-z0-9\._]\*)\.&#39;</span>)<span style="color:#bbb"> </span>package,<span style="color:#bbb"> </span>id<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>SPLIT(content,<span style="color:#bbb"> </span><span style="color:#4070a0">&#39;\n&#39;</span>)<span style="color:#bbb"> </span>line,<span style="color:#bbb"> </span>id<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[github<span style="color:#666">-</span>groovy<span style="color:#666">-</span>files:github.contents]<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">WHERE</span><span style="color:#bbb"> </span>content<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">CONTAINS</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#39;import&#39;</span><span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">HAVING</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">LEFT</span>(line,<span style="color:#bbb"> </span><span style="color:#40a070">6</span>)<span style="color:#666">=</span><span style="color:#4070a0">&#39;import&#39;</span><span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">GROUP</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">BY</span><span style="color:#bbb"> </span>package,<span style="color:#bbb"> </span>id<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">WHERE</span><span style="color:#bbb"> </span>package<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">LIKE</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#39;groovy.%&#39;</span><span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">GROUP</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">BY</span><span style="color:#bbb"> </span><span style="color:#40a070">1</span><span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">ORDER</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">BY</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">DESC</span><span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">LIMIT</span><span style="color:#bbb"> </span><span style="color:#40a070">10</span>;<span style="color:#bbb"> 
</span></span></span></code></pre></div><p>And <code>groovy.transform</code> is unsurprisingly the winner, as it’s where all Groovy AST transformations reside, providing useful code generation capabilities saving developers from writing tedious repetitive code for common tasks (<code>@Immutable</code>, <code>@Delegate</code>, etc.) After transforms come <code>groovy.util.logging</code> for logging, <code>groovy.json</code> for working with JSON files, <code>groovy.sql</code> for interacting with databases through JDBC, <code>groovy.xml</code> to parse and produce XML payloads, and <code>groovy.text</code> for templating engines:</p>
<p><figure>
  <a href="#img-18fa7ae12a79c5553bb7156790aab30e">
    <img src="/img/bq-groovy/groovy-packages.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-18fa7ae12a79c5553bb7156790aab30e">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bq-groovy/groovy-packages.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>With Groovy AST transformations being so prominent, we can also look at the most frequently used AST transformations with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>TOP(class_name,<span style="color:#bbb"> </span><span style="color:#40a070">10</span>)<span style="color:#bbb"> </span>class_name,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">COUNT</span>(<span style="color:#666">*</span>)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span><span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>REGEXP_EXTRACT(line,<span style="color:#bbb"> </span>r<span style="color:#4070a0">&#39; [a-z0-9\._]*\.([a-zA-Z0-9_]*)&#39;</span>)<span style="color:#bbb"> </span>class_name,<span style="color:#bbb"> </span>id<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>SPLIT(content,<span style="color:#bbb"> </span><span style="color:#4070a0">&#39;\n&#39;</span>)<span style="color:#bbb"> </span>line,<span style="color:#bbb"> </span>id<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[github<span style="color:#666">-</span>groovy<span style="color:#666">-</span>files:github.contents]<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">WHERE</span><span style="color:#bbb"> </span>content<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">CONTAINS</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#39;import&#39;</span><span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">WHERE</span><span style="color:#bbb"> </span>line<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">LIKE</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#39;%groovy.transform.%&#39;</span><span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">GROUP</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">BY</span><span style="color:#bbb"> </span>class_name,<span style="color:#bbb"> </span>id<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">WHERE</span><span style="color:#bbb"> </span>class_name<span style="color:#bbb"> </span><span style="color:#666">!=</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#39;null&#39;</span><span style="color:#bbb"> 
</span></span></span></code></pre></div><p>And we get:</p>
<p><figure>
  <a href="#img-869231108d31b0ca74b1254069f1a506">
    <img src="/img/bq-groovy/frequent-ast-xforms.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-869231108d31b0ca74b1254069f1a506">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/bq-groovy/frequent-ast-xforms.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>The <code>@CompileStatic</code> transformation is the king! Followed by <code>@ToString</code> and <code>@EqualsAndHashCode</code>. But then <code>@TypeChecked</code> is fourth, showing that the static typing and compilation support of Groovy is really heavily used. Other interesting transforms used follow with <code>@Canonical</code>, <code>@PackageScope</code>, <code>@InheritConstructors</code>, <code>@Immutable</code> or <code>@TupleConstructor</code>.</p>
<p>As I was exploring imports, I also wondered whether aliased imports was often seen or not:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>aliased,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">count</span>(aliased)<span style="color:#bbb"> </span>total<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>REGEXP<span style="">\</span>_MATCH(line,<span style="color:#bbb"> </span>r<span style="color:#4070a0">&#39;.* (as) .*&#39;</span>)<span style="color:#bbb"> </span>aliased<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>(<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">SELECT</span><span style="color:#bbb"> </span>SPLIT(content,<span style="color:#bbb"> </span><span style="color:#4070a0">&#39;\n&#39;</span>)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">AS</span><span style="color:#bbb"> </span>line<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">FROM</span><span style="color:#bbb"> </span>[github<span style="color:#666">-</span>groovy<span style="color:#666">-</span>files:github.contents]<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>)<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">WHERE</span><span style="color:#bbb"> </span>line<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">CONTAINS</span><span style="color:#bbb"> </span><span style="color:#4070a0">&#39;import &#39;</span><span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>)<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">GROUP</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">BY</span><span style="color:#bbb"> </span>aliased<span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">LIMIT</span><span style="color:#bbb"> </span><span style="color:#40a070">100</span><span style="color:#bbb"> 
</span></span></span></code></pre></div><p>Interestingly, there are 2719 aliased imports over 765281 non aliased ones, that’s about 0.36%, so roughly 1 <code>import … as …</code> for 300 normal imports.</p>
<p>And with that, that rounds up my exploration of Groovy source files on Github! It’s your turn to play with the dataset, and see if there are interesting findings to be unveiled! Did you find anything?</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Tale of a Groovy Spark in the Cloud</title><link>https://glaforge.dev/posts/2016/06/20/tale-of-a-groovy-spark-in-the-cloud/</link><pubDate>Mon, 20 Jun 2016 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2016/06/20/tale-of-a-groovy-spark-in-the-cloud/</guid><description>&lt;p>As I recently &lt;a href="https://glaforge.dev/posts/2016/06/02/joining-google-as-a-developer-advocate-for-the-google-cloud-platform/">joined Google&lt;/a>’s developer advocacy team for &lt;a href="https://cloud.google.com/">Google Cloud Platform&lt;/a>, I thought I could have a little bit of fun with combining my passion for &lt;a href="http://groovy-lang.org/">Apache Groovy&lt;/a> with some cool cloudy stuff from Google! Incidentally, Paolo Di Tommaso tweeted about his own &lt;a href="https://twitter.com/PaoloDiTommaso/status/741290125947240448">experiments with using Groovy with Apache Spark&lt;/a>, and shared his &lt;a href="https://github.com/pditommaso/gspark/blob/master/src/main/groovy/org/apache/spark/examples/GroovySparkPi.groovy">code on Github&lt;/a>:&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-a17d06a4778a0f0b0305e4977327d2ec">
&lt;img src="https://glaforge.dev/img/spark-groovy/gspark-01.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-a17d06a4778a0f0b0305e4977327d2ec">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/spark-groovy/gspark-01.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>I thought that would be a nice fun first little project to try to use Groovy to run a Spark job on Google Cloud &lt;a href="https://cloud.google.com/dataproc/">Dataproc&lt;/a>! Dataproc manages Hadoop &amp;amp; Spark for you: it’s a service that provides managed Apache Hadoop, Apache Spark, Apache Pig and Apache Hive. You can easily process big datasets at low cost, control those costs by quickly creating managed clusters of any size and turning them off where you’re done. In addition, you can obviously use all the other Google Cloud Platform services and products from Dataproc (ie. store the big datasets in Google Cloud Storage, on HDFS, through BigQuery, etc.)&lt;/p></description><content:encoded>
<![CDATA[<p>As I recently <a href="https://glaforge.dev/posts/2016/06/02/joining-google-as-a-developer-advocate-for-the-google-cloud-platform/">joined Google</a>’s developer advocacy team for <a href="https://cloud.google.com/">Google Cloud Platform</a>, I thought I could have a little bit of fun with combining my passion for <a href="http://groovy-lang.org/">Apache Groovy</a> with some cool cloudy stuff from Google! Incidentally, Paolo Di Tommaso tweeted about his own <a href="https://twitter.com/PaoloDiTommaso/status/741290125947240448">experiments with using Groovy with Apache Spark</a>, and shared his <a href="https://github.com/pditommaso/gspark/blob/master/src/main/groovy/org/apache/spark/examples/GroovySparkPi.groovy">code on Github</a>:</p>
<p><figure>
  <a href="#img-a17d06a4778a0f0b0305e4977327d2ec">
    <img src="/img/spark-groovy/gspark-01.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-a17d06a4778a0f0b0305e4977327d2ec">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/spark-groovy/gspark-01.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I thought that would be a nice fun first little project to try to use Groovy to run a Spark job on Google Cloud <a href="https://cloud.google.com/dataproc/">Dataproc</a>! Dataproc manages Hadoop &amp; Spark for you: it’s a service that provides managed Apache Hadoop, Apache Spark, Apache Pig and Apache Hive. You can easily process big datasets at low cost, control those costs by quickly creating managed clusters of any size and turning them off where you’re done. In addition, you can obviously use all the other Google Cloud Platform services and products from Dataproc (ie. store the big datasets in Google Cloud Storage, on HDFS, through BigQuery, etc.)</p>
<p>More concretely,, how do you run a Groovy job in Google Cloud Dataproc’s managed Spark service? Let’s see that in action!</p>
<p>To get started, I checked out Paolo’s <a href="https://github.com/pditommaso/gspark">samples</a> from Github, and I even groovy-fied the Pi calculation example (based on this <a href="https://en.wikipedia.org/wiki/Approximations_of_%CF%80#Summing_a_circle.27s_area">approach</a>) to make it a bit more idiomatic:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">package</span> org<span style="color:#666">.</span><span style="color:#4070a0">apache</span><span style="color:#666">.</span><span style="color:#4070a0">spark</span><span style="color:#666">.</span><span style="color:#4070a0">examples</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.transform.CompileStatic</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.apache.spark.SparkConf</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.apache.spark.api.java.JavaSparkContext</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.apache.spark.api.java.function.Function</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.apache.spark.api.java.function.Function2</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">scala.Function0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@CompileStatic</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">final</span> <span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">GroovySparkPi</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#007020;font-weight:bold">static</span> <span style="color:#902000">void</span> <span style="color:#06287e">main</span><span style="color:#666">(</span>String<span style="color:#666">[]</span> args<span style="color:#666">)</span> <span style="color:#007020;font-weight:bold">throws</span> Exception <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>   <span style="color:#902000">def</span> sparkConf <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> SparkConf<span style="color:#666">().</span><span style="color:#4070a0">setAppName</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;GroovySparkPi&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>   <span style="color:#902000">def</span> jsc <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> JavaSparkContext<span style="color:#666">(</span>sparkConf<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>   <span style="color:#902000">int</span> slices <span style="color:#666">=</span> <span style="color:#666">(</span>args<span style="color:#666">.</span><span style="color:#4070a0">length</span> <span style="color:#666">==</span> <span style="color:#40a070">1</span><span style="color:#666">)</span> <span style="color:#666">?</span> Integer<span style="color:#666">.</span><span style="color:#4070a0">parseInt</span><span style="color:#666">(</span>args<span style="color:#666">[</span><span style="color:#40a070">0</span><span style="color:#666">])</span> <span style="color:#666">:</span> <span style="color:#40a070">2</span>
</span></span><span style="display:flex;"><span>   <span style="color:#902000">int</span> n <span style="color:#666">=</span> <span style="color:#40a070">100000</span> <span style="color:#666">*</span> slices
</span></span><span style="display:flex;"><span>   <span style="color:#902000">def</span> dataSet <span style="color:#666">=</span> jsc<span style="color:#666">.</span><span style="color:#4070a0">parallelize</span><span style="color:#666">(</span><span style="color:#40a070">0</span><span style="color:#666">..</span>
</span></span><span style="display:flex;"><span>   <span style="color:#902000">def</span> mapper <span style="color:#666">=</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>     <span style="color:#902000">double</span> x <span style="color:#666">=</span> Math<span style="color:#666">.</span><span style="color:#4070a0">random</span><span style="color:#666">()</span> <span style="color:#666">*</span> <span style="color:#40a070">2</span> <span style="color:#666">-</span> <span style="color:#40a070">1</span>
</span></span><span style="display:flex;"><span>     <span style="color:#902000">double</span> y <span style="color:#666">=</span> Math<span style="color:#666">.</span><span style="color:#4070a0">random</span><span style="color:#666">()</span> <span style="color:#666">*</span> <span style="color:#40a070">2</span> <span style="color:#666">-</span> <span style="color:#40a070">1</span>
</span></span><span style="display:flex;"><span>     <span style="color:#007020;font-weight:bold">return</span> <span style="color:#666">(</span>x <span style="color:#666">*</span> x <span style="color:#666">+</span> y <span style="color:#666">*</span> y <span style="color:#666">&lt;</span> <span style="color:#40a070">1</span><span style="color:#666">)</span> <span style="color:#666">?</span> <span style="color:#40a070">1</span> <span style="color:#666">:</span> <span style="color:#40a070">0</span>
</span></span><span style="display:flex;"><span>   <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>   <span style="color:#902000">int</span> count <span style="color:#666">=</span> dataSet
</span></span><span style="display:flex;"><span>           <span style="color:#666">.</span><span style="color:#4070a0">map</span><span style="color:#666">(</span>mapper <span style="color:#007020;font-weight:bold">as</span> Function<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>           <span style="color:#666">.</span><span style="color:#4070a0">reduce</span><span style="color:#666">({</span><span style="color:#902000">int</span> a<span style="color:#666">,</span> <span style="color:#902000">int</span> b <span style="color:#666">-&gt;</span> a <span style="color:#666">+</span> b<span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">as</span> Function2<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>   println <span style="color:#4070a0">&#34;Pi is roughly ${4.0 * count / n}&#34;</span>
</span></span><span style="display:flex;"><span>   jsc<span style="color:#666">.</span><span style="color:#4070a0">stop</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span> 
</span></span></code></pre></div><p>You can also use a Groovy script instead of a full-blown class, but you need to make the script serializable with a little trick, by specifying a custom base script class. You need a custom Serializable Script:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.transform.BaseScript</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@BaseScript</span> SerializableScript baseScript
</span></span></code></pre></div><p>And in your job script, you should specify this is your base script class with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">abstract</span> <span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">SerializableScript</span> <span style="color:#007020;font-weight:bold">extends</span> Script <span style="color:#007020;font-weight:bold">implements</span> Serializable <span style="color:#666">{}</span>
</span></span></code></pre></div><p>The project comes with a Gradle build file, so you can compile and build your project with the gradle jar command to quickly create a JAR archive.</p>
<p>Now let’s focus on the Cloud Dataproc part of the story! I basically simply followed the <a href="https://cloud.google.com/dataproc/quickstarts/quickstart-console">quickstart guide</a>. I used the Console (the UI web interface), but you could as well use the <a href="https://cloud.google.com/sdk/gcloud/">gcloud</a> command-line tool as well. You’ll need an account of course, and enable billing, as running Spark jobs on clusters can be potentially expensive, but don’t fear, there’s a <a href="https://cloud.google.com/free-trial">free trial</a> that you can take advantage of! You can also do some quick computation with the <a href="https://cloud.google.com/products/calculator">calculator</a> to estimate how much a certain workload will cost you. In my case, as a one time off job, this is a sub-dollar bill that I have to pay.</p>
<p>Let’s create a brand new project:</p>
<p><figure>
  <a href="#img-0bac8a10fdc73817f3b3883b112b9f5b">
    <img src="/img/spark-groovy/gspark-02.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-0bac8a10fdc73817f3b3883b112b9f5b">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/spark-groovy/gspark-02.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>We’re going to create a Spark cluster, but we’ll need to enable the Compute Engine API for this to work, so head over to the hamburger menu, select the API manager item, and enable it:</p>
<p><figure>
  <a href="#img-6278d895917e2dcc8696529d29dd2aaf">
    <img src="/img/spark-groovy/gspark-04.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-6278d895917e2dcc8696529d29dd2aaf">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/spark-groovy/gspark-04.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Select the Dataproc menu from the hamburger, which will allow you to create a brand new Spark cluster:</p>
<p><figure>
  <a href="#img-5a6f9845e3ca88984754108ee052e619">
    <img src="/img/spark-groovy/gspark-03.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-5a6f9845e3ca88984754108ee052e619">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/spark-groovy/gspark-03.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Create a cluster as follows (the smallest one possible for our demo):</p>
<p><figure>
  <a href="#img-dfef9bb8f30e15f0b973ea50206b983e">
    <img src="/img/spark-groovy/gspark-05.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-dfef9bb8f30e15f0b973ea50206b983e">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/spark-groovy/gspark-05.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Also, in case you have some heavy &amp; expensive workloads, for which it doesn’t matter much if they can be interrupted or not (and then relaunched later on), you could also use <a href="https://cloud.google.com/preemptible-vms/">Preemptible VMs</a> to further lower the cost.</p>
<p>We created a JAR archive for our Groovy Spark demo, and for the purpose of this demo, we’ll push the JAR into Google Cloud Storage, to create Spark jobs with this JAR (but there are other ways to push your job’s code automatically as well). From the menu again, go to Cloud Storage, and create a new bucket:</p>
<p><figure>
  <a href="#img-4960b98e4f6507b19ce6f5717c49760d">
    <img src="/img/spark-groovy/gspark-14.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-4960b98e4f6507b19ce6f5717c49760d">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/spark-groovy/gspark-14.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Create a bucket with a name of your choice (we’ll need to remember it when creating the Spark jobs):</p>
<p><figure>
  <a href="#img-466b47d42731421e53d17f7cf132d407">
    <img src="/img/spark-groovy/gspark-15.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-466b47d42731421e53d17f7cf132d407">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/spark-groovy/gspark-15.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Once this bucket is created, click on it, and then click on the “upload files” button, to upload your JAR file:</p>
<p><figure>
  <a href="#img-12a13df3927722513491419e78c4f036">
    <img src="/img/spark-groovy/gspark-16.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-12a13df3927722513491419e78c4f036">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/spark-groovy/gspark-16.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>We can come back to the Dataproc section, clicking on the Jobs sub-menu to create a new job:</p>
<p><figure>
  <a href="#img-1ec7bafd091e486a4bbc969c79ea2998">
    <img src="/img/spark-groovy/gspark-26.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-1ec7bafd091e486a4bbc969c79ea2998">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/spark-groovy/gspark-26.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>We’ll create a new job, using our recently created cluster. We’ll need to specify the location of the JAR containing our Spark job: we’ll use the URL <code>gs://groovy-spark-demo-jar/spark-groovy-1.1.jar</code>. The <code>gs://</code> part corresponds to the Google Cloud Storage protocol, as that’s where we’re hosting our JAR. Then groovy-spark-demo-jar/ corresponds to the name of the bucket we created, and then at the end, the name of the JAR file. We’ll use an argument of 1000 to specify the number of parallel computations of our Pi approximation algorithm we want to run:</p>
<p><figure>
  <a href="#img-a64f159aa14b3fe8efa1876888b8f206">
    <img src="/img/spark-groovy/gspark-27.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-a64f159aa14b3fe8efa1876888b8f206">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/spark-groovy/gspark-27.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Click <code>Submit</code>, and here we go, our Groovy Spark job is running in the cloud on our 2-node cluster!</p>
<p><figure>
  <a href="#img-c7d6334ddcb1c0ceffad83100cc708fc">
    <img src="/img/spark-groovy/gspark-28.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-c7d6334ddcb1c0ceffad83100cc708fc">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/spark-groovy/gspark-28.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Just a bit of setup through the console, which you can also do from the command-line, and of course a bit of Groovy code to do the computation. Be sure to have a look at the <a href="https://cloud.google.com/dataproc/quickstarts/quickstart-console">quick start guide</a>, which gives more details than this blog post, and you can look at some other <a href="https://github.com/pditommaso/gspark/tree/master/src/main/groovy/org/apache/spark/examples">Groovy Spark samples</a> thanks to Paolo on his Github project.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Joining Google as a Developer Advocate for the Google Cloud Platform</title><link>https://glaforge.dev/posts/2016/06/02/joining-google-as-a-developer-advocate-for-the-google-cloud-platform/</link><pubDate>Thu, 02 Jun 2016 11:01:39 +0100</pubDate><guid>https://glaforge.dev/posts/2016/06/02/joining-google-as-a-developer-advocate-for-the-google-cloud-platform/</guid><description>&lt;p>The cat is out the bag: I&amp;rsquo;m joining Google on June 6th, as a &lt;strong>Developer Advocate&lt;/strong> for the &lt;strong>Google Cloud Platform&lt;/strong> team!&lt;/p>
&lt;p>My Groovy friends will likely remember when I launched &lt;a href="http://gaelyk.org/">Gaelyk&lt;/a>, a lightweight toolkit for developing Groovy apps on Google App Engine? Since then, I&amp;rsquo;ve always been a big fan of the &lt;a href="https://cloud.google.com/">Google Cloud Platform&lt;/a> (although it wasn&amp;rsquo;t called that way then) and followed the latest developments of the whole platform. And wohhh, so many new&lt;a href="https://cloud.google.com/products/"> services and products&lt;/a> have seen the light of day since my early experiments with App Engine! So there will be a lot to learn, a lot to do, and thus, a lot to advocate! &lt;/p></description><content:encoded>
<![CDATA[<p>The cat is out the bag: I&rsquo;m joining Google on June 6th, as a <strong>Developer Advocate</strong> for the <strong>Google Cloud Platform</strong> team!</p>
<p>My Groovy friends will likely remember when I launched <a href="http://gaelyk.org/">Gaelyk</a>, a lightweight toolkit for developing Groovy apps on Google App Engine? Since then, I&rsquo;ve always been a big fan of the <a href="https://cloud.google.com/">Google Cloud Platform</a> (although it wasn&rsquo;t called that way then) and followed the latest developments of the whole platform. And wohhh, so many new<a href="https://cloud.google.com/products/"> services and products</a> have seen the light of day since my early experiments with App Engine! So there will be a lot to learn, a lot to do, and thus, a lot to advocate! </p>
<p>I&rsquo;m really happy and excited to join the <a href="https://cloud.google.com/solutions/">Google Cloud Platform</a> team! I&rsquo;m looking forward to working with my new team, and join some good old Googler friends I&rsquo;ve came to know throughout my career.</p>
<p>I&rsquo;ve been really pleased to work with my friends at <a href="https://restlet.com/">Restlet</a> for the past year and a half, made friends, and learnt a lot along the way working with my colleagues. I wish them luck for their great <a href="https://restlet.com/platform/">Web API platform</a>, and I&rsquo;ll continue to follow their progress!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A Groovy journey in Open Source land (GR8Conf Europe)</title><link>https://glaforge.dev/talks/2016/06/02/a-groovy-journey-in-open-source-land-gr8conf-europe/</link><pubDate>Thu, 02 Jun 2016 00:00:00 +0200</pubDate><guid>https://glaforge.dev/talks/2016/06/02/a-groovy-journey-in-open-source-land-gr8conf-europe/</guid><description>&lt;p>Direct live from &lt;a href="http://gr8conf.eu/#/">GR8Conf Europe 2016&lt;/a>, in Copenhagen, Denmark! This morning, I presented my latest &lt;a href="https://speakerdeck.com/glaforge/a-groovy-journey-in-open-source-land">update about the Apache Groovy history, and the latest developments&lt;/a> in the 2.4.x and future 2.5 branches.&lt;br />
Abstract:&lt;/p>
&lt;blockquote>
&lt;p>In dog years&amp;hellip; err&amp;hellip; Open Source years, the Groovy programming language project is a very mature and successful one, as its 12 million downloads a year can attest. The Groovy language is certainly the most widely deployed alternative language of the JVM today. But how do we go from a hobby night &amp;amp; week-end project to professionally company sponsored? And back again to hobby mode but joining the wider Apache Software Foundation community?&lt;/p></description><content:encoded>
<![CDATA[<p>Direct live from <a href="http://gr8conf.eu/#/">GR8Conf Europe 2016</a>, in Copenhagen, Denmark! This morning, I presented my latest <a href="https://speakerdeck.com/glaforge/a-groovy-journey-in-open-source-land">update about the Apache Groovy history, and the latest developments</a> in the 2.4.x and future 2.5 branches.<br />
Abstract:</p>
<blockquote>
<p>In dog years&hellip; err&hellip; Open Source years, the Groovy programming language project is a very mature and successful one, as its 12 million downloads a year can attest. The Groovy language is certainly the most widely deployed alternative language of the JVM today. But how do we go from a hobby night &amp; week-end project to professionally company sponsored? And back again to hobby mode but joining the wider Apache Software Foundation community?</p>
<p>Guillaume will guide you through the history of the project, its latest developments, and its recent news, outlining the importance of a community around an Open Source project.</p>
<p>Also, we&rsquo;ll discuss what it means to contribute, when it&rsquo;s your hobby or as a paid committer &ndash; what does it change? What it means to join the Apache community, what the impact of professional Open Source is, and more.</p></blockquote>
<p>And here are the slides:</p>
<script async class="speakerdeck-embed" data-id="beca9cd406a84e7c8f4a34374cdb458c" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Get in the flow! The API developer workflow!</title><link>https://glaforge.dev/talks/2016/05/26/get-in-the-flow-the-api-developer-workflow/</link><pubDate>Thu, 26 May 2016 00:02:00 +0200</pubDate><guid>https://glaforge.dev/talks/2016/05/26/get-in-the-flow-the-api-developer-workflow/</guid><description>&lt;p>What are the activities of the Web API developer? How API tooling should not get in the way of developer&amp;rsquo;s productivity? I presented a talk on this topic at the GlueCon conference:&lt;/p>
&lt;blockquote>
&lt;p>The API ecosystem provides powerful tools, online services and definition formats for designing, testing, running, or managing APIs. All share common purposes: improve our productivity when developing an API, allow us to collaborate more effectively, or share our creations with the world!&lt;/p></description><content:encoded>
<![CDATA[<p>What are the activities of the Web API developer? How API tooling should not get in the way of developer&rsquo;s productivity? I presented a talk on this topic at the GlueCon conference:</p>
<blockquote>
<p>The API ecosystem provides powerful tools, online services and definition formats for designing, testing, running, or managing APIs. All share common purposes: improve our productivity when developing an API, allow us to collaborate more effectively, or share our creations with the world!</p>
<p>But developers have already invented efficient tactics to streamline their development, gathered experience with and sharpened their tools of trade. The result is that the services or formats mentioned before can actually also get in their way, and interrupt their development flow, as they have to resort to get out of their routine and processes, to use them.</p>
<p>What can API tooling vendors do to reconcile the habits of developers with their tools? In this session, Guillaume Laforge, Restlet&rsquo;s Product Ninja &amp; Advocate, will talk about building, versioning &amp; dependency management of API artifacts, scenario &amp; conformance testing, API documentation, continuous integration, multi-environment continuous deployment, and team collaboration! Let’s get back into the development flow!</p></blockquote>
<script async class="speakerdeck-embed" data-id="66dea0c6069e42969fd55002089b11f7" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A five-sided prism polarizing Web API development</title><link>https://glaforge.dev/talks/2016/05/26/a-five-sided-prism-polarizing-web-api-development/</link><pubDate>Thu, 26 May 2016 00:00:00 +0200</pubDate><guid>https://glaforge.dev/talks/2016/05/26/a-five-sided-prism-polarizing-web-api-development/</guid><description>&lt;p>At GlueCon, I presented about the 5-sided prism that polarizes Web API development:&lt;/p>
&lt;blockquote>
&lt;p>How do you tackle your API development? Are you diving head-first in the code to get something quickly out the door? Do you start by defining the API contract, that you&amp;rsquo;ll share between your teams and the consumers? Perhaps you prefer to describe your acceptance tests, explaining the behavior you expect from your API. But if you&amp;rsquo;re a storyteller, you&amp;rsquo;ll probably write some use cases, scenarios, to have a better feel for what your API is all about, and how your users will take advantage of it. Or simply, you already have data lying around that wants to set free, and be exposed restfully to the world.&lt;/p></description><content:encoded>
<![CDATA[<p>At GlueCon, I presented about the 5-sided prism that polarizes Web API development:</p>
<blockquote>
<p>How do you tackle your API development? Are you diving head-first in the code to get something quickly out the door? Do you start by defining the API contract, that you&rsquo;ll share between your teams and the consumers? Perhaps you prefer to describe your acceptance tests, explaining the behavior you expect from your API. But if you&rsquo;re a storyteller, you&rsquo;ll probably write some use cases, scenarios, to have a better feel for what your API is all about, and how your users will take advantage of it. Or simply, you already have data lying around that wants to set free, and be exposed restfully to the world.</p>
<p>In this session, Guillaume Laforge, Restlet&rsquo;s Product Ninja &amp; Advocate, will highlight different approaches to Web API development, along with their pros &amp; cons. Whether you&rsquo;re starting with code, a contract, tests, documentation, or data, you&rsquo;ll get a glimpse of light into the tasty book of API development recipes.</p></blockquote>
<script async class="speakerdeck-embed" data-id="52db3b6495bf4d7c8a298193fa350284" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A web API for each API consumer?</title><link>https://glaforge.dev/posts/2016/05/07/a-web-api-for-each-api-consumer/</link><pubDate>Sat, 07 May 2016 00:01:00 +0200</pubDate><guid>https://glaforge.dev/posts/2016/05/07/a-web-api-for-each-api-consumer/</guid><description>&lt;p>At our disposal, we have so many ways to interact with an API: from a mobile on iOS or Android, from a web application, or from other services or microservices. And all of them have different needs: one wants only a shallow overview of the data, while the other desires a detailed view of a certain resource and all its sub-resources. It&amp;rsquo;s becoming difficult to design an API that caters to the needs of those varied consumers.&lt;/p></description><content:encoded>
<![CDATA[<p>At our disposal, we have so many ways to interact with an API: from a mobile on iOS or Android, from a web application, or from other services or microservices. And all of them have different needs: one wants only a shallow overview of the data, while the other desires a detailed view of a certain resource and all its sub-resources. It&rsquo;s becoming difficult to design an API that caters to the needs of those varied consumers.</p>
<p>So what can we do? There&rsquo;s a trend around providing different API facades for each consumer, as Netflix does with its <a href="http://www.infoq.com/articles/api-facades">experience and ephemeral APIs</a>, or how Sam Newman described it in its <a href="http://samnewman.io/patterns/architectural/bff/">Backends for Frontends</a> pattern. But such an approach increases the maintenance burden and complexity, and might only really make sense for big enough teams.</p>
<p>Other approaches exist for customizing payloads for different consumers, without really providing as many derived API facades as you have API consumers. You can take advantage of the Prefer header, fields filtering, custom Mime media types, hypermedia, or Facebook&rsquo;s GraphQL approach.</p>
<p>I&rsquo;ve written an article for InfoQ that dives into this topic, and covers all those subjects: &ldquo;<a href="http://www.infoq.com/articles/api-facades">One API, many facades</a>&rdquo;.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>How far should API definition languages go?</title><link>https://glaforge.dev/posts/2016/05/07/how-far-should-api-definition-languages-go/</link><pubDate>Sat, 07 May 2016 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2016/05/07/how-far-should-api-definition-languages-go/</guid><description>&lt;p>I had the pleasure of writing an &lt;a href="http://nordicapis.com/how-far-should-api-definition-languages-go/">article&lt;/a> for Nordic APIs on Web API definition languages.&lt;/p>
&lt;p>If you&amp;rsquo;re into the world of Web APIs, you&amp;rsquo;ve probably heard of formats like &lt;a href="http://swagger.io/">Swagger&lt;/a>, &lt;a href="http://raml.org/">RAML&lt;/a> or &lt;a href="https://apiblueprint.org/">API Blueprint&lt;/a>. They allow developers to define the contract of the API, with its endpoints, its resources, its representations, allowed methods, the kind of payloads it understands, the status codes returned, and more.&lt;/p>
&lt;p>With the contract of your Web API, you can generate code for your backend implementation or client kits, documentation for publishing the details of your API for your API consumers. This contract becomes a key element of your API strategy: a contract between the frontend team and backend team to be sure to work on the same ground, between your tech team implementing the public API of your company and all the API consumers that will interact with it.&lt;/p></description><content:encoded>
<![CDATA[<p>I had the pleasure of writing an <a href="http://nordicapis.com/how-far-should-api-definition-languages-go/">article</a> for Nordic APIs on Web API definition languages.</p>
<p>If you&rsquo;re into the world of Web APIs, you&rsquo;ve probably heard of formats like <a href="http://swagger.io/">Swagger</a>, <a href="http://raml.org/">RAML</a> or <a href="https://apiblueprint.org/">API Blueprint</a>. They allow developers to define the contract of the API, with its endpoints, its resources, its representations, allowed methods, the kind of payloads it understands, the status codes returned, and more.</p>
<p>With the contract of your Web API, you can generate code for your backend implementation or client kits, documentation for publishing the details of your API for your API consumers. This contract becomes a key element of your API strategy: a contract between the frontend team and backend team to be sure to work on the same ground, between your tech team implementing the public API of your company and all the API consumers that will interact with it.</p>
<p>But API definition languages don&rsquo;t necessarily denote all the fineness of your API, in particular its business rules, or how to check that an implementation conforms to a contract or if the implementation follows the style guides of your company, what tests could exhibit the behavior of the API, and more. So the questions I asked myself was <a href="http://nordicapis.com/how-far-should-api-definition-languages-go/">how far should API definition languages go</a>!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>How far should API definition languages go</title><link>https://glaforge.dev/posts/2016/04/27/how-far-should-api-definition-languages-go/</link><pubDate>Wed, 27 Apr 2016 14:46:34 +0100</pubDate><guid>https://glaforge.dev/posts/2016/04/27/how-far-should-api-definition-languages-go/</guid><description>&lt;p>&lt;figure>
&lt;a href="#img-f36b72b70a9c8aaf92f35990de96a187">
&lt;img src="https://nordicapis.com/wp-content/uploads/How-far-should-api-definition-languages-go-1.png"
alt="How-far-should-api-definition-languages-go"
/>
&lt;/a>
&lt;figcaption>How-far-should-api-definition-languages-go&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-f36b72b70a9c8aaf92f35990de96a187">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://nordicapis.com/wp-content/uploads/How-far-should-api-definition-languages-go-1.png"
alt="How-far-should-api-definition-languages-go"
/>
&lt;div class="lightbox-caption">How-far-should-api-definition-languages-go&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>The most common API definition languages we spot in the wild are &lt;a href="https://swagger.io/">Swagger&lt;/a> / &lt;a href="https://openapis.org/">OpenAPI Spec&lt;/a>, &lt;a href="https://raml.org/">RAML&lt;/a> and &lt;a href="https://apiblueprint.org/">API Blueprint&lt;/a>. All three let you define your endpoints, your resources, your query or path parameters, your headers, status codes, security schemes, and more.&lt;/p>
&lt;p>In a nutshell, these definition languages define the &lt;strong>structure&lt;/strong> of your API, and allow you to describe many &lt;strong>elements&lt;/strong>. As standards in the API industry evolve, however, their purpose and design are under continuous scrutiny. Specifically, the extensibility of API specifications with additional elements and feature sets comes into question.&lt;/p></description><content:encoded>
<![CDATA[<p><figure>
  <a href="#img-f36b72b70a9c8aaf92f35990de96a187">
    <img src="https://nordicapis.com/wp-content/uploads/How-far-should-api-definition-languages-go-1.png"
      alt="How-far-should-api-definition-languages-go"
       />
  </a>
  <figcaption>How-far-should-api-definition-languages-go</figcaption>
</figure>
<div class="lightbox" id="img-f36b72b70a9c8aaf92f35990de96a187">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://nordicapis.com/wp-content/uploads/How-far-should-api-definition-languages-go-1.png"
    alt="How-far-should-api-definition-languages-go"
     />
  <div class="lightbox-caption">How-far-should-api-definition-languages-go</div>
</div>
</p>
<p>The most common API definition languages we spot in the wild are <a href="https://swagger.io/">Swagger</a> / <a href="https://openapis.org/">OpenAPI Spec</a>, <a href="https://raml.org/">RAML</a> and <a href="https://apiblueprint.org/">API Blueprint</a>. All three let you define your endpoints, your resources, your query or path parameters, your headers, status codes, security schemes, and more.</p>
<p>In a nutshell, these definition languages define the <strong>structure</strong> of your API, and allow you to describe many <strong>elements</strong>. As standards in the API industry evolve, however, their purpose and design are under continuous scrutiny. Specifically, the extensibility of API specifications with additional elements and feature sets comes into question.</p>
<p>Recently, a discussion sparked on the <a href="https://nordicapis.com/open-api-initiative-means-api-space/">Open API Specification</a> issue tracker about the <a href="https://nordicapis.com/evolution-openapi-specification-openapi-mean-open-world/">Open World vs Closed World principles</a>. In a nutshell, the question is whether an API definition is supposed to describe an API fully (including, for example, the paths that you&rsquo;re not allowed to access, all the possible status codes, etc.) or whether it is just a best effort to describe what&rsquo;s usable and needed by most API consumers &mdash; not necessarily covering the whole scope of the API.</p>
<p>All the recent conversation has us thinking &mdash; <strong>how detailed should API definitions be</strong>?. One can dream up many other uses for an API definition language:</p>
<ul>
<li>API definitions could bring additional information or documentation, with a narrative and more metadata describing the flow of API calls, how the callers will be able to interact with the API.</li>
<li>API providers can bring such extra metadata for the purpose of API discovery, to give credits (about IP or copyright information), perhaps residing in external descriptors like <a href="https://apisjson.org/">APIs.json</a> (a format to help discover metadata about an API).</li>
<li>JSON payloads comply with JSON schemas, but data models could be defined regardless of the media types being chosen by the consumer, so that the consumer could actually request any output format, but still understand the intrinsic nature of the resources which are dealt with.</li>
<li>There are often common traits spreading across an API, for example how to do pagination, and some API definitions are able to factor this common aspect in a reusable and referenceable fashion.</li>
<li>Hypermedia APIs are not necessarily well covered in terms of modeling throughout the most common API specifications, there&rsquo;s certainly something to be improved here.</li>
<li>Another interesting topic is also about how to ensure that an API implementation conforms with its API definition, or follows some some good conventions or company guidelines. Tooling could be provided to help with this task.</li>
</ul>
<p><a href="https://raml.org/blogs/update-raml-10-current-status">RAML is approaching 1.0</a>, <a href="https://github.com/OAI/OpenAPI-Specification/issues?q=is%3Aissue+is%3Aopen+label%3A%22OpenAPI.Next+Proposal%22">OpenAPI Spec is being worked on</a> with a list of issues detailing what to expect, and <a href="https://github.com/apiaryio/api-blueprint/wiki/Roadmap">API Blueprint published its roadmap</a>. As our favorite specifications will soon see interesting evolutions, it&rsquo;s a great time to revisit the dialectic on API definition language design.</p>
<p>Let&rsquo;s look a little bit closer at some of the propositions above to see if API definitions provide solutions, and discuss if it&rsquo;s even the purpose of an API definition language to do these sort of things. What could we build into the next generation of API specifications to make them truly remarkable?</p>
<h2 id="api-storytelling-modeling-multi-step-transactions-within-documentation">API Storytelling: Modeling Multi-Step Transactions within Documentation</h2>
<p>API providers need to be storytellers. When you design an API for a user base, you want to tell them a story &mdash; how they access the API with an API key, how they interact with the API, and what the common traits for various resources are (pagination).</p>
<p>Often, to conduct a certain business use case you must issue several calls in a row: first you get the details of an order, then you ask for the details of the customer, and lastly perhaps their delivery address. So you <strong>chain calls</strong>, using elements of the response of the previous call to craft the subsequent request. Sometimes, you have different payloads, with or without embedded entities; an order call may contain all the details of the customer and its address, or it may not.</p>
<p>How can a provider explain the logical scenario of calls? A page published within the API documentation should describe such scenarios, and regroup logical series of calls together in a coherent story &mdash; the use cases of the API. Though you can provide descriptions of API parameters using current definition languages, there is no way to tie elements together in a cohesive <strong>workflow</strong>. An API definition with the power to tell <strong>API stories</strong> that describe common instances of multi-step transactions could be very powerful.</p>
<h2 id="test-scenarios">Test Scenarios</h2>
<p>Related to these use cases, having <strong>tests</strong> corresponding to those scenarios would be handy. For instance, <a href="https://nordicapis.com/10-continuous-integration-tools-spur-api-development/#dhc">DHC by Restlet</a> has its own test scenario definition format that you can use to run tests from a Maven plugin, or within your <a href="https://nordicapis.com/reach-devops-zen-with-these-continuous-integration-tools/">Continuous Integration pipeline</a> with the Jenkins server. Could such scenario tests be derived from the API definition, or embedded within the API definition itself? Interestingly, the <a href="https://github.com/apiaryio/api-blueprint/issues/21">API Blueprint roadmap lists scenarios and testing</a>, so this might be coming sooner than later.</p>
<h2 id="documentation-vs-specification">Documentation vs Specification</h2>
<p>It&rsquo;s easy to mix up the difference between the <strong>API definition format</strong> with the published <strong>API documentation</strong>. Though they <em>are</em> different entities, you can generate the documentation from the definition &mdash; there&rsquo;s no denying the two are intimately linked. If they were completely separate, API definition formats wouldn&rsquo;t even have any description tags at all, as the format would probably only be used for <a href="https://nordicapis.com/designing-apis-machines/">machine-consumption</a>, and not for <a href="https://nordicapis.com/designing-apis-humans/">human-consumption</a>.</p>
<p>Using API definitions, you can derive implementation for skeletons for your <a href="https://restlet.com/blog/2015/04/28/easy-client-sdk-and-server-skeleton-generation-for-your-apis/">API or client SDKs</a>. But you can also generate beautiful and comprehensive documentation: perhaps just static HTML documentation, or spiced up with sprinkles of JavaScript to provide an interactive playground to try out the API directly, or go full steam with hosting that definition in a full-blown <a href="https://nordicapis.com/beautiful-ui-design-for-api-developer-portals/">API developer portal</a> where you could add collaboration capabilities, versioning information, and more. As the end user documentation as well as <a href="https://nordicapis.com/description-agnostic-api-development-with-api-transformer/">SDKs and libraries</a> can be derived form the core API definition, the two are inherently linked.</p>
<h2 id="style-guide-conformance-and-tooling">Style Guide, Conformance and Tooling</h2>
<p>In designing an API, your company probably has already created a set of guidelines, or <strong>best practices</strong> to follow. For instance, look at <a href="https://github.com/paypal/api-standards/blob/master/api-style-guide.md">Paypal&rsquo;s API style guide</a>. The document describes the general structures of URIs, a mandatory namespace element, a paging method that should remain consistent, and more. Your own guidelines might specify naming conventions, like the use of camel-case.</p>
<p><strong>It would be handy to automatically assess if the API actually conforms to those guidelines</strong>. Maintaining this type of conformance could be part of the API definition itself, or exist as an external document that is referenced from the API definition. This could require a new format that describes these conventions rules, leading one to consider if such guidelines should be declaratively-defined, or if a scripting language could be used for more advanced validation needs.</p>
<p>Along with having a special guideline description file, tools would be needed to check that the implementation of the API really conforms to those guidelines! This tooling would likely not be part of an API definition format, but could accompany it, and be offered in various languages and technology stacks that anyone can feel at home with.</p>
<h2 id="hypermedia">Hypermedia</h2>
<p>One of the key tenets of the <strong>REST</strong> architectural style coined by Roy Fielding is hypermedia, with hypermedia as the engine of application state (HATEOAS). More APIs are conforming to this constraint, and several approaches exist to define those hypermedia links &mdash; <a href="https://nordicapis.com/designing-evolvable-apis-for-the-web-formats/#hypermediatypesforwebapis">we&rsquo;ve defined HAL, JSON-LD, and Siren hypermedia types in a past blog post</a>.</p>
<p>Even though the concept is popular, API languages often don&rsquo;t let you easily describe such hypermedia-driven APIs. Shouldn&rsquo;t they help users describe such metadata and hyperlinks? Zdenek Nemec of Apiary gave an example <a href="https://gist.github.com/zdne/988a54c03609655f47b7#file-rendered-md">implementation of HAL for an API</a>, using the new MSON modeling capabilities of API Blueprint. Perhaps in the near future, API languages will support a particular flag to denote what hypermedia approach the API is using.</p>
<h2 id="data-and-payload-modeling">Data and Payload Modeling</h2>
<p>Speaking of <a href="https://github.com/apiaryio/mson">MSON</a> (Markdown Syntax for Object Notation), it&rsquo;s also an interesting take on modeling your API payloads. With a convenient and readable format, which is neither JSON schema nor XML schema, you can describe what your data will look like, regardless of the underlying media type being used.</p>
<p>This beckons the question whether any possible payload can be supported that way. After all, esoteric APIs will probably not conform with JSON schema, XML schema, or MSON. There are likely going to be expressivity differences between them all. Should API definition languages be prescriptive, and limit to the 80/20 sane use cases of data modeling?</p>
<p>Another example is Apigee&rsquo;s recent <a href="https://apigee.com/about/blog/developer/rapier-cut-through-tedium-api-specification">Rapier API specification</a>, whose goal is to describe data-oriented APIs with entities and their relationships. Rapier might possibly be contributed to the OpenAPI Specification at some point, but there are clear limits in terms of expressivity.</p>
<h2 id="api-definition-extensions">API Definition Extensions</h2>
<p>Although API definition languages don&rsquo;t necessarily provide all those bells and whistles, an important aspect is <strong>extension</strong>. For example, <a href="https://swagger.io/specification/#vendorExtensions">Swagger / OpenAPI Spec provides the notion of extensions</a>. API designers have the ability to define custom fields (following a convention) within which they can stuff any data or metadata they want! So if you want to add guideline documentation pointers, or associated tests, you could very well add them as custom extensions. Then, your own tools can grok those specific extensions and deal with them however you like.</p>
<p>For instance, <a href="https://restlet.com/blog/2015/10/26/importing-and-translating-raml-api-definition/">tools translating from one API language to another</a> can store custom extensions that describe how to best do the translation, in particular in the case that one specification doesn&rsquo;t support certain metadata that the other supports. Let&rsquo;s keep in mind one downside of custom extensions though: They are not understood by all the tools and vendors, and are somewhat proprietary and non portable. So use at your own risk!</p>
<h2 id="final-thoughts">Final Thoughts</h2>
<p>Right, so shoud API definition languages provide everything and the kitchen sink? If so, what&rsquo;s the threshold? Where do we put the cursor? Or &mdash; should an API definition simply stick to describing the various API elements, and then leverage a complementary format like <a href="https://apisjson.org/">APIs.json</a> to add and link to additional resources?</p>
<p>It is apparent that there are benefits to pushing API definition languages a bit further and providing more information than they do. Extensions are the way to go, as long as we define, publish and share such extensions in a somewhat standardized fashion. But some definitely feel the need for going further than what is offered today. What&rsquo;s your take on that? Where would you put the cursor?</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>One API, many facades?</title><link>https://glaforge.dev/posts/2016/03/13/one-api-many-facades/</link><pubDate>Sun, 13 Mar 2016 14:36:34 +0100</pubDate><guid>https://glaforge.dev/posts/2016/03/13/one-api-many-facades/</guid><description>&lt;p>An interesting trend is emerging in the world of Web APIs, with various engineers and companies advocating for dedicated APIs for each consumer with particular needs. Imagine a world where your system needs to expose not only one API for iOS, one API for Android, one for the website, and one for the AngularJS app front end, but also APIs for various set-top boxes and exotic mobile platforms or for third-party companies that call your API. Beyond any ideal design of your API, reality strikes back with the concrete and differing concerns of varied API consumers. You might need to optimize your API accordingly.&lt;/p></description><content:encoded>
<![CDATA[<p>An interesting trend is emerging in the world of Web APIs, with various engineers and companies advocating for dedicated APIs for each consumer with particular needs. Imagine a world where your system needs to expose not only one API for iOS, one API for Android, one for the website, and one for the AngularJS app front end, but also APIs for various set-top boxes and exotic mobile platforms or for third-party companies that call your API. Beyond any ideal design of your API, reality strikes back with the concrete and differing concerns of varied API consumers. You might need to optimize your API accordingly.</p>
<h2 id="experience-apis">Experience APIs</h2>
<p>On InfoQ, Jérôme Louvel (chief geek and co-founder of <a href="http://restlet.com/">Restlet</a>) interviewed Daniel Jacobson (vice president of edge engineering at <a href="http://www.netflix.com/">Netflix</a>) about Netflix&rsquo;s e<a href="http://www.infoq.com/news/2015/11/daniel-jacobson-ephemeral-apis">xperience APIs and ephemeral APIs</a>. Daniel&rsquo;s team is responsible for handling all traffic for signup, discovery, and playback of videos on devices around the world. With the concept of experience APIs, Netflix creates special APIs to handle optimized responses for a given requesting agent. With ephemeral APIs, Netflix engineers iteratively transition and evolve those experience APIs.</p>
<p>The goal of experience APIs is to solve problems Netflix has encountered while scaling its platform to more than 60 million consumers on dozens of devices with different characteristics. Offering such dedicated APIs allowed Netflix to provide the best user experiences possible across all devices and to optimize bandwidth according to a device&rsquo;s screen size for less latency and data consumption. It let Netflix engineers progress rapidly and independently from the core back-end teams, in isolation, with their own versioning scheme and orchestrating their own deployments.</p>
<p>At Netflix, dedicated teams take care of those experience APIs. It&rsquo;s not the central or core API team that is responsible for all the APIs derived for each and every possible client. There aren&rsquo;t many companies with the same scale as Netflix, and just because Netflix builds dedicated APIs for all API consumers doesn&rsquo;t mean it makes sense for your own context. For small shops, maintaining and evolving too many API front ends could be ineffective or even an anti-pattern, as the cost would be pretty high. Netflix had to build a special API platform to support this approach.</p>
<h2 id="microservices-architectures">Microservices architectures</h2>
<p>With the trend towards microservices-based architectures, the <a href="http://microservices.io/patterns/apigateway.html">API gateway</a> or API facade is making a resurgence. Your architecture is scattered among several small services and you need to have front-facing services responsible for exposing an API to consumers. So it&rsquo;s not all that surprising that by having many microservices you can also have multiple facades for your consumers.</p>
<p>A gateway or facade will let a consumer make just one call instead of forcing the consumer to make multiple calls to several underlying microservices. This alone is easier for the API consumer and the benefit increases with a smarter gateway or façade that takes advantage of caching (because multiple calls still need to be made), applies security concerns (authentication, authorization), or implements rules (rate limitation, IP filtering). The API provider can control how the consumers use its API.</p>
<p>Gateways, from vendors or built in-house like Netflix&rsquo;s, also add complementary value like edge services (an API infrastructure in the DMZ of the company can be used in novel and interesting ways, like how Netflix uses Zuul for multiregion resiliency) or pipelining or filter chaining (which help extract crosscutting concerns and implement enterprise-wide patterns).</p>
<h2 id="back-ends-for-front-ends">Back ends for front ends</h2>
<p>In a <a href="http://www.infoq.com/news/2015/12/bff-backend-frontend-pattern">recent article</a>, Sam Newman looks at this approach of dedicated consumer APIs as a pattern named &ldquo;<a href="http://samnewman.io/patterns/architectural/bff/">Backends for Frontends</a>&rdquo; (BfFs). Instead of a general-purpose API for all clients, you can have several BfFs: one for a Web front end, one for mobile clients (or even one for iOS and one for Android), and more.</p>
<p><a href="https://www.thoughtworks.com/insights/blog/bff-soundcloud">SoundCloud adopted the BfF pattern</a>, with API front ends for the iOS platform, the Android platform, the website, and for web-embedding. As is the case at Netflix, it seems that this technique works best when there are dedicated teams responsible for those front ends. If you have only one team taking care of the back end and its API, you&rsquo;d better not overload them with a large number of variations for different consumers.</p>
<p>Getting back to microservices, BfF can also make sense for migration: when migrating a monolith to microservices, one BfF can call into the monolith while other BfFs could call the new microservices instead, following the <a href="http://www.martinfowler.com/bliki/StranglerApplication.html">Strangler pattern</a> where you progressively move away from the legacy code to adopt newer evolutions.</p>
<p>A monolith is complex, easily accumulates technical debt, and mixes too many concerns at the same time, while microservices help you focus on one particular concern at a time. But microservices architectures also have drawbacks. You have to operate and orchestrate them, and they may have to evolve at a different pace from one big monolith. Maintaining consistency between all services in such distributed systems is not easy, either. Communication among many microservices might introduce additional latency because of communication delay. The consensus around the duplication and denormalization of data for each microservice can also complicate data management and consistency. <a href="http://highscalability.com/blog/2014/4/8/microservices-not-a-free-lunch.html">Microservices are not a free lunch</a>, and you can read more on some microservices anti-patterns with Vijay Alagarasan&rsquo;s <a href="http://www.infoq.com/articles/seven-uservices-antipatterns">anti-patterns article</a>, or on Tareq Abedrabbo&rsquo;s &ldquo;<a href="https://www.opencredo.com/2014/11/19/7-deadly-sins-of-microservices/">The 7 Deadly Sins of Microservices</a>&rdquo;.</p>
<p>The deciding factor for choosing to use experience APIs or BfF could very well be having dedicated teams for them or not. If you&rsquo;re small and have only one team to take care of the back end and the front-facing or edge Web APIs, it might be more complicated for you to take care of the many variants (think maintenance costs) but if you&rsquo;re large enough, teams can more easily take ownership of these front-end APIs and evolve them at their own pace.</p>
<h2 id="apis-as-a-team-communication-pattern">APIs as a team-communication pattern</h2>
<p>While companies are organized as teams, I see more and more instances where developers are separated into front-end developers (whether Web or mobile) and back-end developers who implement the APIs needed for the Web or mobile devices. Web APIs have become central to the way projects are delivered: APIs are the contract that binds the different teams together and allows them to collaborate efficiently.</p>
<p>When developing an API that is going to be used by others, it&rsquo;s important not to break that contract. Often, frameworks and tools allow you to generate an API definition from the codebase &mdash; for example, with an annotation-driven approach where you label your endpoints, query parameters, etc. with annotations. But sometimes, even if your own test cases still pass, the smallest code refactoring could very well break the contract. Your codebase might be fine, but the refactoring might have broken the code of your API consumers. To collaborate more effectively, consider going with an API-contract-first approach and make sure your implementation still conforms with the shared agreement: the API definition. There are different API definition languages available and popular these days, like <a href="http://swagger.io/">Swagger</a> (<a href="https://github.com/OAI/OpenAPI-Specification">Open API specification</a>), <a href="http://raml.org/">RAML</a>, or <a href="https://apiblueprint.org/">API Blueprint</a>. Pick one you&rsquo;re comfortable with.</p>
<p>Working with an API definition has a few advantages. First of all, it should make it more difficult to break compatibility as your implementation has to conform to the API definition. Secondly, API definitions are pretty well equipped in terms of tooling. From API definitions, you can generate client SDKs that your API consumers can integrate in their projects to call your API or even server skeletons to generate the initial implementation of your service. You can also create mocks of your APIs, which developers can easily call while the underlying API is being built, without juggling the different development cycles of the producers and consumers of APIs. Each team can work at its own pace! However, it&rsquo;s not just about code or compatibility but also about documentation. API-definition languages also help when documenting your API, with nicely generated documentation that shows the various endpoints, query parameters, etc., as well as (sometimes) offering an interactive console, which allows you to easily craft calls to the API.</p>
<h2 id="different-payloads-for-different-consumers">Different payloads for different consumers</h2>
<p>Adopting an API-contract-first approach is certainly helpful and provides benefits, but what can you do when different clients have different API needs? Particularly, if you don&rsquo;t have the luxury of dedicated teams taking ownership of different API facades, how can you make your API meet the needs of all your API consumers?</p>
<p>In a recent article on InfoQ, Jean-Jacques Dubray explained why he <a href="http://www.infoq.com/articles/no-more-mvc-frameworks">stopped using MVC frameworks</a>. In the introduction, he explained how mobile or front-end developers frequently asked for APIs tailored for their UI needs, regardless of a sound data model for the underlying business concepts. The state-action-model (SAM) pattern that Dubray described nicely supports the BfF approach. SAM is a new, reactive functional pattern which simplifies fronted architectures by clearly separating the business logic from the effects, in particular decoupling back-end APIs from the view. As the state and model are separate from actions and views, the actions can be specific for a given front end or not appear at all: it&rsquo;s up to you to decide where to put the cursor. You may also generate the state representation or view from the central back end or by those intermediary facades.</p>
<p>A website or single-page application might need to display a detailed view of a product and all its reviews, but perhaps a mobile device will only show the product details and its rating, letting the mobile user tap to load the reviews afterwards. Depending on the UI, the flow, the actions available, the level of detail, and the entities retrieved might be different. Typically, you&rsquo;d like to diminish the number of API calls to retrieve data on a mobile device because of connectivity and bandwidth constraints, and you want the payload returned to contain only what&rsquo;s required and nothing more. But this doesn&rsquo;t matter that much for the Web front end, and with asynchronous calls, you&rsquo;re totally fine with loading more content or resources lazily. In either case, APIs should obviously respond rapidly, and have a good service-level agreement. But what are the options for delivering multiple customized APIs to different consumers?</p>
<h2 id="specific-endpoints-query-parameters-and-fields-filtering">Specific endpoints, query parameters, and fields filtering</h2>
<p>A basic approach could be to provide different endpoints (<code>/api/mobile/movie</code> versus <code>/api/web/movie</code>) or even simply query parameters (<code>/api/movie?format=full</code> or <code>/api/movie?format=mobile</code>), but there are perhaps more elegant solutions.</p>
<p>Similar to query parameters, your API might be able to customize returned payloads by letting the consumer decide which fields he or she wants, like: <code>/api/movie?fields=title,kind,rating</code>, or <code>/api/movie?exclude=actors</code>.</p>
<p>With fields filtering, you may decide also if you want to get related resources in response: <code>/api/movie?includes=actors.name</code>.</p>
<h2 id="custom-mime-media-types">Custom MIME media types</h2>
<p>As the implementor of the API, you have options. You might decide not to offer any customization at all! Consumers will either have to go with what you offer or wrap your API inside their own facade into which they&rsquo;ve built the customization they want. But since you&rsquo;re a great person, you could offer them profiles: you can be creative with media types and offer a leaner or richer payload depending on the media type a consumer requests. For instance, if you look at the <a href="https://developer.github.com/v3/media/">GitHub API</a>, you&rsquo;ll notice types like: <code>application/vnd.github.v3.full+json</code></p>
<p>Along with a &ldquo;full&rdquo; profile that offers the whole payload and related entities, you can provide a &ldquo;mobile&rdquo; variant, and perhaps a &ldquo;minimal&rdquo; one too.</p>
<p>The API consumer makes a call that requests the media type that fits his/her use case the most.</p>
<h2 id="prefer-header">Prefer header</h2>
<p>Irakli Nadareishvili wrote about <a href="http://www.freshblurbs.com/blog/2015/06/25/api-representations-prefer.html">client-optimized resource representations in APIs</a>, mentioning a lesser-known header field: the Prefer header (<a href="https://tools.ietf.org/html/rfc7240">RFC 7240</a>).</p>
<p>As with custom media types, a client would request a certain profile using the Prefer header: using <code>Prefer: return=mobile</code> would have the API reply with a customized payload and the header <code>Preference-Applied: return=mobile</code>. Note that the Vary header should also mention that the Prefer header is available.</p>
<p>Depending on whether you, the API developer, want to be in charge of deciding what kind of payloads you support, you might like the custom media type, the Prefer header, or dedicated endpoints. If you want to let clients decide more explicitly what kind of fields and relationships to retrieve, you could opt for field filtering or query parameters.</p>
<h2 id="graphql">GraphQL</h2>
<p>With its <a href="https://facebook.github.io/react/">React</a> view framework, Facebook <a href="https://facebook.github.io/react/blog/2015/05/01/graphql-introduction.html">introduced</a> developers to <a href="https://facebook.github.io/graphql/">GraphQL</a>. Here, consumers are in total control of what they&rsquo;ll receive: the fields and relationships. The consumer issues a call that specifies what the return payload should look like:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-graphql" data-lang="graphql"><span style="display:flex;"><span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>user(id:<span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">3500401</span>)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>id,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>name,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>isViewerFriend,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>profilePicture(size:<span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">50</span>)<span style="color:#bbb">  </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>uri,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>width,<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">      </span>height<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>And the API should reply with the following payload:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;user&#34;</span> : {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;id&#34;</span>: <span style="color:#40a070">3500401</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;name&#34;</span>: <span style="color:#4070a0">&#34;Jing Chen&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;isViewerFriend&#34;</span>: <span style="color:#007020;font-weight:bold">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;profilePicture&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;uri&#34;</span>: <span style="color:#4070a0">&#34;http://someurl.cdn/pic.jpg&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;width&#34;</span>: <span style="color:#40a070">50</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;height&#34;</span>: <span style="color:#40a070">50</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>GraphQL is at the same time a query and a description of what you&rsquo;d like the answer to this query to be. GraphQL lets API consumers totally control what they&rsquo;ll get in return, offering the highest level of flexibility.</p>
<p>A similar approach exists in specifications like OData, which lets you customize the payloads with <a href="http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/using-select-expand-and-value">
  <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi><mi>e</mi><mi>l</mi><mi>e</mi><mi>c</mi><mi>t</mi><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">select, </annotation></semantics></math></span>

expand, and $value parameters</a>. But OData has not really caught on and might be on the verge of abandonment; <a href="http://www.ben-morris.com/netflix-has-abandoned-odata-does-the-standard-have-a-future-without-an-ecosystem/">Netflix and eBay stopped supporting OData</a> a while ago. That said, other actors like Microsoft and SalesForce do still support it.</p>
<h2 id="hypermedia-apis">Hypermedia APIs</h2>
<p>One last option to explore is hypermedia APIs. When thinking of hypermedia APIs, you often think of all the additional hyperlinks that clutter responses and that could easily double the payload size. Payload size and number of calls really matter to a mobile device. Despite that, it&rsquo;s important to think of hypermedia through HATEOAS (Hypermedia as the engine of application state), a core tenet of REST APIs that is often overlooked. It&rsquo;s about the capabilities offered by the API. A consumer will have access to related resources, but links offered through those hypermedia relations can also be about giving different profiles to choose from, like:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;_links&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;self&#34;</span>: { <span style="color:#062873;font-weight:bold">&#34;href&#34;</span>: <span style="color:#4070a0">&#34;/movie/123&#34;</span> },
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&#34;mobile&#34;</span>: { <span style="color:#062873;font-weight:bold">&#34;href&#34;</span>: <span style="color:#4070a0">&#34;/m/movie/123&#34;</span> },
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Additionally, certain hypermedia approaches fully embrace the notion of embedding related entities. Hydra, HAL, and SIREN provide the ability to embed sub-entities, so that you could retrieve a particular movie and the embedded list of all the actors in that movie.</p>
<p>From an article on <a href="http://sookocheff.com/post/api/on-choosing-a-hypermedia-format/">how to choose a hypermedia format</a>, Kevin Sookocheff gives an example showing how accessing a &ldquo;player&rsquo;s list of friends&rdquo; resource also embeds the actual representations of those friends and not just links to those individual resources, thus eliminating calls to each friend resource:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&#34;_links&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;self&#34;</span>: { <span style="color:#062873;font-weight:bold">&#34;href&#34;</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#34;https://api.example.com/player/1234567890/friends&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;size&#34;</span>: <span style="color:#4070a0">&#34;2&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&#34;_embedded&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&#34;player&#34;</span>: [
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&#34;_links&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&#34;self&#34;</span>: { <span style="color:#062873;font-weight:bold">&#34;href&#34;</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#4070a0">&#34;https://api.example.com/player/1895638109&#34;</span> },
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&#34;friends&#34;</span>: { <span style="color:#062873;font-weight:bold">&#34;href&#34;</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#4070a0">&#34;https://api.example.com/player/1895638109/friends&#34;</span> }
</span></span><span style="display:flex;"><span>          },
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&#34;playerId&#34;</span>: <span style="color:#4070a0">&#34;1895638109&#34;</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&#34;name&#34;</span>: <span style="color:#4070a0">&#34;Sheldon Dong&#34;</span>
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&#34;_links&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&#34;self&#34;</span>: { <span style="color:#062873;font-weight:bold">&#34;href&#34;</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#4070a0">&#34;https://api.example.com/player/8371023509&#34;</span> },
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&#34;friends&#34;</span>: { <span style="color:#062873;font-weight:bold">&#34;href&#34;</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#4070a0">&#34;https://api.example.com/player/8371023509/friends&#34;</span> }
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&#34;playerId&#34;</span>: <span style="color:#4070a0">&#34;8371023509&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&#34;name&#34;</span>: <span style="color:#4070a0">&#34;Martin Liu&#34;</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="summary">Summary</h2>
<p>Web APIs increasingly have several kinds of consumers with different needs. Microservice architectures can encourage us to deploy fine-grained API facades for those needs (the so-called experience APIs or BfF patterns), but this can become an anti-pattern if you have too many distinct consumers to please, especially if you&rsquo;ve got only a small team to take care of all those front ends.</p>
<p>Be sure to do the math! Before going one way or another, you have to study the cost of your options and whether or not you can support them. <a href="http://www.ebpml.org/blog15/2013/11/understanding-the-costs-of-versioning-an-api-or-a-service/">Creating different variants of an API has a cost</a>, for the implementor as well as for the consumer, that depends on the adopted strategy. Also, once you&rsquo;ve unleashed your API and given it to its consumers, perhaps it&rsquo;s also time to rethink  and refactor this API, as maybe you didn&rsquo;t take those special device or consumer requirements well enough into account during the design phase.</p>
<p>If you have dedicated teams for these API facades, then it&rsquo;s an option to consider. When you don&rsquo;t have that luxury, there are other ways to customize payloads for your consumers without the induced complexity, with simple tricks like field filtering or the Prefer header up to full-blown solutions like custom media types or specifications like GraphQL.</p>
<p>But you don&rsquo;t necessarily need to fire the big guns, and could opt for a middle path: one main, full API plus one or two variants for mobile devices, and you are likely going to meet the requirements of all your consumers. Consider including a pinch of field filtering, and everybody will be happy with your APIs!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy default params to avoid one-argument methods being called without params</title><link>https://glaforge.dev/posts/2016/01/08/groovy-default-params-to-avoid-one-argument-methods-being-called-without-params/</link><pubDate>Fri, 08 Jan 2016 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2016/01/08/groovy-default-params-to-avoid-one-argument-methods-being-called-without-params/</guid><description>&lt;p>Recently, I saw an interesting &lt;a href="https://twitter.com/djsmith42/status/679018096334675968">tweet&lt;/a>, mentioning a JavaScript trick using default parameters to make a parameter mandatory.&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-14f0971da0b32c675f86d38763e29374">
&lt;img src="https://glaforge.dev/img/misc/default-param-mandatory-javascript-trick.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-14f0971da0b32c675f86d38763e29374">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/default-param-mandatory-javascript-trick.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>In a language like &lt;a href="http://www.groovy-lang.org">Apache Groovy&lt;/a>, when statically compiled, you&amp;rsquo;d get a compilation error if you forgot a parameter, because the signature couldn&amp;rsquo;t be found by the compiler. In dynamic mode, you&amp;rsquo;d get a runtime error though, with a MissingMethodException (and the error message should give you a hint as to which method you should actually call instead).&lt;/p></description><content:encoded>
<![CDATA[<p>Recently, I saw an interesting <a href="https://twitter.com/djsmith42/status/679018096334675968">tweet</a>, mentioning a JavaScript trick using default parameters to make a parameter mandatory.</p>
<p><figure>
  <a href="#img-14f0971da0b32c675f86d38763e29374">
    <img src="/img/misc/default-param-mandatory-javascript-trick.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-14f0971da0b32c675f86d38763e29374">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/default-param-mandatory-javascript-trick.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>In a language like <a href="http://www.groovy-lang.org">Apache Groovy</a>, when statically compiled, you&rsquo;d get a compilation error if you forgot a parameter, because the signature couldn&rsquo;t be found by the compiler. In dynamic mode, you&rsquo;d get a runtime error though, with a MissingMethodException (and the error message should give you a hint as to which method you should actually call instead).</p>
<p>But there&rsquo;s a particular case of the Groovy method dispatch that&rsquo;s a bit special (and actually something we might be removing at some point in a breaking version of the language, but it&rsquo;s been there since 1.0). When you have single-argument methods, you&rsquo;re allowed to call those methods without passing a parameter! And the parameter is filled simply with null. So you might have made a mistake in your code, forgetting to pass an actual parameter value, but you&rsquo;d get neither a compilation error nor a runtime exception, but a null value.</p>
<p>So I thought about that JavaScript trick, and adapted to this situation, to ensure that you can&rsquo;t call a one-argument method without an argument.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>String <span style="color:#06287e">up</span><span style="color:#666">(</span>String s<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    s<span style="color:#666">?.</span><span style="color:#4070a0">toUpperCase</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">up</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;groovy&#39;</span><span style="color:#666">)</span> <span style="color:#666">==</span> <span style="color:#4070a0">&#39;GROOVY&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// a strange aspect of Groovy is that 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// you can call a one-argument method without passing any actual argument
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// as if you were passing null, as in up(null)
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">up</span><span style="color:#666">()</span> <span style="color:#666">==</span> <span style="color:#007020;font-weight:bold">null</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// let&#39;s use the JavaScript trick with mandatory default params:
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// https://twitter.com/djsmith42/status/679018096334675968
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>
</span></span><span style="display:flex;"><span>String <span style="color:#06287e">up2</span><span style="color:#666">(</span>String s <span style="color:#666">=</span> mandatory<span style="color:#666">(</span><span style="color:#4070a0">&#39;s&#39;</span><span style="color:#666">))</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    s<span style="color:#666">?.</span><span style="color:#4070a0">toUpperCase</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">void</span> <span style="color:#06287e">mandatory</span><span style="color:#666">(</span>String paramName<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">throw</span> <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">Exception</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;Please provide an actual value for &#39;$paramName&#39;&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">up2</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;groovy&#39;</span><span style="color:#666">)</span> <span style="color:#666">==</span> <span style="color:#4070a0">&#39;GROOVY&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    up2<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">catch</span><span style="color:#666">(</span>emAll<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">assert</span> emAll<span style="color:#666">.</span><span style="color:#4070a0">message</span> <span style="color:#666">==</span> <span style="color:#4070a0">&#34;Please provide an actual value for &#39;s&#39;&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// another approach with using a closure call as default value
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>
</span></span><span style="display:flex;"><span>String <span style="color:#06287e">up3</span><span style="color:#666">(</span>String s <span style="color:#666">=</span> <span style="color:#666">{</span> <span style="color:#666">-&gt;</span> <span style="color:#007020;font-weight:bold">throw</span> <span style="color:#007020;font-weight:bold">new</span> Exception<span style="color:#666">(</span><span style="color:#4070a0">&#34;Please provide an actual value for &#39;s&#39;&#34;</span><span style="color:#666">)</span> <span style="color:#666">}()</span> <span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    s<span style="color:#666">?.</span><span style="color:#4070a0">toUpperCase</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">up3</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;groovy&#39;</span><span style="color:#666">)</span> <span style="color:#666">==</span> <span style="color:#4070a0">&#39;GROOVY&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    up3<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">catch</span><span style="color:#666">(</span>emAll<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">assert</span> emAll<span style="color:#666">.</span><span style="color:#4070a0">message</span> <span style="color:#666">==</span> <span style="color:#4070a0">&#34;Please provide an actual value for &#39;s&#39;&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>I&rsquo;ve also pushed that example on the <a href="http://groovyconsole.appspot.com/script/5094328489738240">Groovy Web Console</a>, if you wanna play with it.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #77</title><link>https://glaforge.dev/posts/2015/09/13/groovy-weekly-77/</link><pubDate>Sun, 13 Sep 2015 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2015/09/13/groovy-weekly-77/</guid><description>&lt;p>It’s literally the eve of &lt;a href="http://springone2gx.com/">SpringOne2GX&lt;/a>, in Washington DC! I’m flying tomorrow, for 9+ hours above the Atlantic, to gather with the cool kids of the Groovy ecosystem. I’m impatient to see some of my fellow readers and Groovy users around there.&lt;/p>
&lt;p>And let me wish the &lt;a href="http://griffon-user.74797.x6.nabble.com/Happy-Birthday-Griffon-td48.html">Griffon framework and its team a Groovy birthday&lt;/a>!&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://ratpack.io/versions/1.0.0-rc-2">Ratpack 1.0-rc-2&lt;/a>, a second release candidate&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gradle/status/640174805958856705">Gradle 2.7-rc-2&lt;/a> released&lt;/li>
&lt;li>&lt;a href="https://twitter.com/grailsframework/status/639813351988662272">Grails 3.0.5&lt;/a> released with the new Web API profile&lt;/li>
&lt;li>&lt;a href="https://twitter.com/grailsframework/status/641593634530443264">Grails 3.0.6&lt;/a> released&lt;/li>
&lt;li>&lt;a href="http://spring.io/blog/2015/09/04/spring-boot-1-3-0-m5-available-now">Spring Boot 1.3.0.M5&lt;/a> released&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://sergiodelamo.es/grails-3-webapi-vs-web-profiles/">Grails 3 Web profile vs the new Web API profile&lt;/a>&lt;/li>
&lt;li>How to &lt;a href="https://medium.com/@benorama/how-to-deploy-grails-3-app-to-aws-elastic-beanstalk-with-gradle-and-travis-318d084c0f7d">deploy a Grails 3 app to AWS Beanstalk and CloudFront CDN&lt;/a> by Benoit Hédiard&lt;/li>
&lt;li>MrHaki&amp;rsquo;s Groovy Goodness
&lt;ul>
&lt;li>&lt;a href="http://mrhaki.blogspot.fr/2015/09/groovy-goodness-removing-elements-from.html">Removing elements from a collection&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://mrhaki.blogspot.fr/2015/09/groovy-goodness-inspect-method-returns.html">Inspect method returns nicely formatted object values&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://mrhaki.blogspot.fr/2015/09/groovy-goodness-operator-overloading-in.html">Operator overloading in reverse&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>MrHaki&amp;rsquo;s Spocklight
&lt;ul>
&lt;li>&lt;a href="http://mrhaki.blogspot.fr/2015/09/spocklight-only-run-specs-based-on.html">Only run specs based on conditions&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://mrhaki.blogspot.fr/2015/09/spocklight-undo-metaclass-changes.html">Undo MetaClass changes&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Philip Heath writes about &lt;a href="http://www.thattestingguy.com/spock-the-logical-choice-for-testing-java/">Spock, the logical choice for testing&lt;/a>&lt;/li>
&lt;li>Paul Gross&amp;rsquo; team has &lt;a href="https://www.pgrs.net/2015/09/01/migrating-from-gradle-to-bazel/">migrated from Gradle to Bazel&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="screencasts">Screencasts&lt;/h2>
&lt;ul>
&lt;li>Screencast of &lt;a href="https://twitter.com/netbeans/status/640283206789562368">NetBeans&amp;rsquo; Gradle integration&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="interviews">Interviews&lt;/h2>
&lt;ul>
&lt;li>An interview by JaxEnter of Dierk König in German, where he says that &lt;a href="https://jaxenter.de/groovy-ist-ein-pionier-fuer-neue-ideen-26638">Groovy is a pioneer language&lt;/a> where innovative changes help shape the future of Java and other languages&lt;/li>
&lt;/ul>
&lt;h2 id="mailing-list-posts">Mailing-list posts&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://griffon-user.74797.x6.nabble.com/Happy-Birthday-Griffon-td48.html">Griffon celebrates the 7th anniversary&lt;/a> of the project!&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>The Mars rover has a state called &amp;ldquo;&lt;a href="https://twitter.com/mittie/status/641371571915988992">Solar Groovy&lt;/a>&amp;rdquo; noticed Dierk König&lt;/li>
&lt;li>Marco Vermeulen is excited about the upcoming &lt;a href="https://twitter.com/marc0der/status/640609521531011073">new website for SDKmanager&lt;/a> (aka GVM), based off the Groovy website code and look&lt;/li>
&lt;li>&lt;a href="https://twitter.com/sdkmanager/status/639812197833703424">Grails 3.0.5&lt;/a> available on GVM&lt;/li>
&lt;li>&lt;a href="https://twitter.com/sdkmanager/status/641589310110740480">Grails 3.0.6&lt;/a> available on GVM&lt;/li>
&lt;li>&lt;a href="https://twitter.com/sdkmanager/status/639620148576559104">Spring Boot 1.3.0.M5&lt;/a> available in GVM&lt;/li>
&lt;li>&lt;a href="https://twitter.com/sdkmanager/status/640167344459939841">Gradle 2.7-rc-2&lt;/a> available through GVM&lt;/li>
&lt;li>Bertrand Goetzman working on a Groovy/Grails training is using a &lt;a href="https://twitter.com/bgoetzmann/status/639558195476869120">Groovy DSL with Grain to generate dynamic HTML documentation&lt;/a> from Markdown documents&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/grainframework/status/641197859644305408">Gradle plugin for the Grain&lt;/a> static site generator is available on the Gradle plugin portal&lt;/li>
&lt;li>Benoit Hédiard created a &lt;a href="https://twitter.com/benorama/status/641512438416953344">French-speaking channel in the Grails Slack&lt;/a> community&lt;/li>
&lt;li>Roman Shaposhnik is reminded he loves Groovy when looking at how it can make the &lt;a href="https://twitter.com/rhatr/status/641762008786825217">Apache BigTop project DSL&lt;/a> easy to grok&lt;/li>
&lt;li>Matt Sheehan brings our attention to the &lt;a href="https://twitter.com/sheehan00/status/641622945043980288">new auto-generated Groovy DSL documentation for the Jenkins Job DSL&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="news">News&lt;/h2>
&lt;ul>
&lt;li>Jacob Aae Mikkelsen&amp;rsquo;s &lt;a href="http://grydeske.net/news/show/108">Grails Diary&lt;/a> week 36&lt;/li>
&lt;/ul>
&lt;h2 id="jobs">Jobs&lt;/h2>
&lt;ul>
&lt;li>SmartThings is hiring a cloud engineer with &lt;a href="https://twitter.com/danveloper/status/639622402205425665">Ratpack experience&lt;/a>&lt;/li>
&lt;li>Gilles Laborderie is recruiting for a &lt;a href="https://twitter.com/g_laborderie/status/639465486116331520">Grails job in Paris&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="books">Books&lt;/h2>
&lt;ul>
&lt;li>MrHaki&amp;rsquo;s &lt;a href="http://mrhaki.blogspot.fr/2015/09/spocklight-notebook-is-updated.html">Spocklight notebook updated&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>Only &lt;a href="https://twitter.com/pledbrook/status/639811956765913090">a few days left before the end of the Groovy/Grails eXchange call for paper&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/JavaOneConf/status/641000355740942336">Guillaume Laforge is a JavaOne featured speaker&lt;/a>&lt;/li>
&lt;li>Guillaume Laforge &lt;a href="https://twitter.com/MrsCaroline_C/status/641332943802269696">relaunched the Paris Groovy Grails User Group&lt;/a> introducing talks on the Grails 3 novelties and Groovy in a VoIP context&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>It’s literally the eve of <a href="http://springone2gx.com/">SpringOne2GX</a>, in Washington DC! I’m flying tomorrow, for 9+ hours above the Atlantic, to gather with the cool kids of the Groovy ecosystem. I’m impatient to see some of my fellow readers and Groovy users around there.</p>
<p>And let me wish the <a href="http://griffon-user.74797.x6.nabble.com/Happy-Birthday-Griffon-td48.html">Griffon framework and its team a Groovy birthday</a>!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://ratpack.io/versions/1.0.0-rc-2">Ratpack 1.0-rc-2</a>, a second release candidate</li>
<li><a href="https://twitter.com/gradle/status/640174805958856705">Gradle 2.7-rc-2</a> released</li>
<li><a href="https://twitter.com/grailsframework/status/639813351988662272">Grails 3.0.5</a> released with the new Web API profile</li>
<li><a href="https://twitter.com/grailsframework/status/641593634530443264">Grails 3.0.6</a> released</li>
<li><a href="http://spring.io/blog/2015/09/04/spring-boot-1-3-0-m5-available-now">Spring Boot 1.3.0.M5</a> released</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li><a href="http://sergiodelamo.es/grails-3-webapi-vs-web-profiles/">Grails 3 Web profile vs the new Web API profile</a></li>
<li>How to <a href="https://medium.com/@benorama/how-to-deploy-grails-3-app-to-aws-elastic-beanstalk-with-gradle-and-travis-318d084c0f7d">deploy a Grails 3 app to AWS Beanstalk and CloudFront CDN</a> by Benoit Hédiard</li>
<li>MrHaki&rsquo;s Groovy Goodness
<ul>
<li><a href="http://mrhaki.blogspot.fr/2015/09/groovy-goodness-removing-elements-from.html">Removing elements from a collection</a></li>
<li><a href="http://mrhaki.blogspot.fr/2015/09/groovy-goodness-inspect-method-returns.html">Inspect method returns nicely formatted object values</a></li>
<li><a href="http://mrhaki.blogspot.fr/2015/09/groovy-goodness-operator-overloading-in.html">Operator overloading in reverse</a></li>
</ul>
</li>
<li>MrHaki&rsquo;s Spocklight
<ul>
<li><a href="http://mrhaki.blogspot.fr/2015/09/spocklight-only-run-specs-based-on.html">Only run specs based on conditions</a></li>
<li><a href="http://mrhaki.blogspot.fr/2015/09/spocklight-undo-metaclass-changes.html">Undo MetaClass changes</a></li>
</ul>
</li>
<li>Philip Heath writes about <a href="http://www.thattestingguy.com/spock-the-logical-choice-for-testing-java/">Spock, the logical choice for testing</a></li>
<li>Paul Gross&rsquo; team has <a href="https://www.pgrs.net/2015/09/01/migrating-from-gradle-to-bazel/">migrated from Gradle to Bazel</a></li>
</ul>
<h2 id="screencasts">Screencasts</h2>
<ul>
<li>Screencast of <a href="https://twitter.com/netbeans/status/640283206789562368">NetBeans&rsquo; Gradle integration</a></li>
</ul>
<h2 id="interviews">Interviews</h2>
<ul>
<li>An interview by JaxEnter of Dierk König in German, where he says that <a href="https://jaxenter.de/groovy-ist-ein-pionier-fuer-neue-ideen-26638">Groovy is a pioneer language</a> where innovative changes help shape the future of Java and other languages</li>
</ul>
<h2 id="mailing-list-posts">Mailing-list posts</h2>
<ul>
<li><a href="http://griffon-user.74797.x6.nabble.com/Happy-Birthday-Griffon-td48.html">Griffon celebrates the 7th anniversary</a> of the project!</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>The Mars rover has a state called &ldquo;<a href="https://twitter.com/mittie/status/641371571915988992">Solar Groovy</a>&rdquo; noticed Dierk König</li>
<li>Marco Vermeulen is excited about the upcoming <a href="https://twitter.com/marc0der/status/640609521531011073">new website for SDKmanager</a> (aka GVM), based off the Groovy website code and look</li>
<li><a href="https://twitter.com/sdkmanager/status/639812197833703424">Grails 3.0.5</a> available on GVM</li>
<li><a href="https://twitter.com/sdkmanager/status/641589310110740480">Grails 3.0.6</a> available on GVM</li>
<li><a href="https://twitter.com/sdkmanager/status/639620148576559104">Spring Boot 1.3.0.M5</a> available in GVM</li>
<li><a href="https://twitter.com/sdkmanager/status/640167344459939841">Gradle 2.7-rc-2</a> available through GVM</li>
<li>Bertrand Goetzman working on a Groovy/Grails training is using a <a href="https://twitter.com/bgoetzmann/status/639558195476869120">Groovy DSL with Grain to generate dynamic HTML documentation</a> from Markdown documents</li>
<li>The <a href="https://twitter.com/grainframework/status/641197859644305408">Gradle plugin for the Grain</a> static site generator is available on the Gradle plugin portal</li>
<li>Benoit Hédiard created a <a href="https://twitter.com/benorama/status/641512438416953344">French-speaking channel in the Grails Slack</a> community</li>
<li>Roman Shaposhnik is reminded he loves Groovy when looking at how it can make the <a href="https://twitter.com/rhatr/status/641762008786825217">Apache BigTop project DSL</a> easy to grok</li>
<li>Matt Sheehan brings our attention to the <a href="https://twitter.com/sheehan00/status/641622945043980288">new auto-generated Groovy DSL documentation for the Jenkins Job DSL</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="http://grydeske.net/news/show/108">Grails Diary</a> week 36</li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li>SmartThings is hiring a cloud engineer with <a href="https://twitter.com/danveloper/status/639622402205425665">Ratpack experience</a></li>
<li>Gilles Laborderie is recruiting for a <a href="https://twitter.com/g_laborderie/status/639465486116331520">Grails job in Paris</a></li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>MrHaki&rsquo;s <a href="http://mrhaki.blogspot.fr/2015/09/spocklight-notebook-is-updated.html">Spocklight notebook updated</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>Only <a href="https://twitter.com/pledbrook/status/639811956765913090">a few days left before the end of the Groovy/Grails eXchange call for paper</a></li>
<li><a href="https://twitter.com/JavaOneConf/status/641000355740942336">Guillaume Laforge is a JavaOne featured speaker</a></li>
<li>Guillaume Laforge <a href="https://twitter.com/MrsCaroline_C/status/641332943802269696">relaunched the Paris Groovy Grails User Group</a> introducing talks on the Grails 3 novelties and Groovy in a VoIP context</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #76</title><link>https://glaforge.dev/posts/2015/09/01/groovy-weekly-76/</link><pubDate>Tue, 01 Sep 2015 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2015/09/01/groovy-weekly-76/</guid><description>&lt;p>Is it really already September? Time flies so fast in the Groovy ecosystem, and although the northern hemisphere was taking some Summer vacation breaks (at least that’s what I did!) there was quite a bit of interesting content published!&lt;/p>
&lt;p>Regarding events, after successful and busy Greach, GR8Conf US/EU conferences, we’re getting ready for September’s &lt;a href="http://springone2gx.com/">SpringOne2GX&lt;/a> in Washington! There’s still time to register and join the Groovy ecosystem affictionados! And I’m really eager to seeing you there.&lt;/p></description><content:encoded>
<![CDATA[<p>Is it really already September? Time flies so fast in the Groovy ecosystem, and although the northern hemisphere was taking some Summer vacation breaks (at least that’s what I did!) there was quite a bit of interesting content published!</p>
<p>Regarding events, after successful and busy Greach, GR8Conf US/EU conferences, we’re getting ready for September’s <a href="http://springone2gx.com/">SpringOne2GX</a> in Washington! There’s still time to register and join the Groovy ecosystem affictionados! And I’m really eager to seeing you there.</p>
<p>The Call for Papers for <a href="https://twitter.com/farjinaarad/status/633979624733601792">Grails India</a> and <a href="https://twitter.com/pledbrook/status/637255460643581952">Groovy Grails eXchange</a> London are also open, for those who wish to submit presentations.</p>
<p>If you’re using Gradle for build automation, check out all the videos of the <a href="http://gradle.org/category/video/">presentations of the recent Gradle Summit</a> which should all be available online.</p>
<p>Happy 3rd birthday to <a href="http://grooscript.org/">GrooScript</a>!</p>
<p>Congrats to the Griffon team for the <a href="https://twitter.com/theaviary/status/638545829826023424">2.4</a> release and Ratpack for its <a href="http://ratpack.io/versions/1.0.0-rc-1">RC</a> before 1.0!</p>
<p>And check out the updated books from MrHaki on <a href="http://mrhaki.blogspot.fr/2015/08/groovy-goodness-notebook-is-updated.html">Groovy</a>, <a href="http://mrhaki.blogspot.fr/2015/08/grails-goodness-notebook-updated.html">Grails</a> and <a href="http://mrhaki.blogspot.fr/2015/08/gradle-goodness-notebook-updated.html">Gradle</a>, and Duncan Dickinson’s updated <a href="https://leanpub.com/groovytutorial">Groovy 2 tutorial</a> on LeanPub.</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://twitter.com/theaviary/status/638545829826023424">Griffon 2.4.0</a> released</li>
<li><a href="http://ratpack.io/versions/1.0.0-rc-1">Ratpack 1.0.0-rc-1</a> released, the last step before the final 1.0!</li>
<li><a href="https://discuss.gradle.org/t/gradle-2-7-rc-1-is-now-available-for-testing/11382">Gradle 2.7-rc-1</a> available for testing</li>
<li><a href="http://www.grengine.ch/">Grengine 1.0.4</a> released</li>
<li><a href="https://groups.google.com/forum/#!topic/spockframework/dkw0uHmOpuM">Spock Reports 1.2.6</a> released</li>
<li><a href="https://twitter.com/grainframework/status/637193955512315904">Grain 0.6.6</a> released</li>
<li><a href="https://groups.google.com/forum/#!topic/geb-user/ac_DvJzNr0A">Geb 0.12.2</a> released</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Keegan Witt, Groovy committer and author of the GMavenPlus Maven plugin, <a href="http://wittykeegan.blogspot.fr/2015/08/a-maven-plugin-developers-thoughts-on.html">contrasts Maven and Gradle</a></li>
<li><a href="http://wittykeegan.blogspot.dk/2015/07/custom-configuration-script-asts.html">Custom configuration script ASTs</a> by Keegan Witt</li>
<li>The tale of Cédric Champeau <a href="http://melix.github.io/blog/2015/08/permgenleak.html">fighting PermGen leaks</a> with Gradle, Groovy, CodeNarc</li>
<li>Kyle Boon shares part 2 of his <a href="http://kyleboon.org/blog/2015/08/14/zero-to-ratpack-part-2/">Zero to Ratpack</a> series</li>
<li>Ted Vinke writes about <a href="https://tedvinke.wordpress.com/2015/08/15/groovy-weekend-collections-splitting-or-collating-a-list/">splitting and collating lists in Groovy</a></li>
<li><a href="http://jkutner.github.io/2015/08/17/deploy-gradle-ratpack-heroku-docker.html">Deploying Gradle apps to Heroku with Docker</a> by Joe Kutner</li>
<li>André Steingreß <a href="http://blog.andresteingress.com/2015/08/21/grails-gpars-and-hibernate/">mixes GPars, Grails and Hibernate</a> together</li>
<li>Andrés Almiray on <a href="http://www.jroller.com/aalmiray/entry/griffon_webfont_icons_on_desktop">using webfonts icons in Griffon</a></li>
<li>Andrés Almirays shows how to <a href="http://www.jroller.com/aalmiray/entry/griffon_stacking_webfont_icons">stack web font icons in Griffon</a></li>
<li><a href="https://objectpartners.com/2015/08/13/sharing-grails-hal-and-json-renderers/">Sharing Grails HAL and JSON renderers</a> by Patrick Double</li>
<li>Søren Berg Glasius demonstrates using <a href="http://sbglasius.tumblr.com/post/126896829137/digetauthwithrestclientbuilder">Grails&rsquo; RestClientBuilder for digest auth</a></li>
<li>Android and <a href="http://tech.ticketmaster.com/2015/07/24/2015-year-of-the-android/">Gradle at TicketMaster</a></li>
<li>MrHaki&rsquo;s Spocklight
<ul>
<li><a href="http://mrhaki.blogspot.fr/2015/08/spocklight-optimize-run-order-test.html">optimize run order test methods</a></li>
<li><a href="http://mrhaki.blogspot.fr/2015/08/spocklight-include-or-exclude.html">include or exclude specifications based on class or interface</a></li>
<li><a href="http://mrhaki.blogspot.fr/2015/08/spocklight-including-or-excluding.html">including or excluding specifications based on annotations</a></li>
<li><a href="http://mrhaki.blogspot.fr/2015/09/spocklight-auto-cleanup-resources.html">auto cleanup resources</a></li>
</ul>
</li>
<li>MrHaki&rsquo;s Gradle goodness:
<ul>
<li><a href="http://mrhaki.blogspot.fr/2015/08/gradle-goodness-quickly-open-test.html">quickly open test report in IntelliJ IDEA</a></li>
<li><a href="http://mrhaki.blogspot.fr/2015/08/gradle-goodness-using-continuous-build.html">using the continuous build feature</a></li>
<li><a href="http://mrhaki.blogspot.fr/2015/08/show-logback-configuration-status-with.html">Show Logback configuration status with Groovy configuration</a> by MrHaki</li>
</ul>
</li>
<li>IntelliJ IDEA 15 EAP features <a href="http://blog.jetbrains.com/idea/2015/08/intellij-idea-15-eap-groovy-builder-ast-transformation-support/">support for the Groovy @Builder</a> AST transformation</li>
<li>A <a href="http://codereview.stackexchange.com/questions/96824/a-groovy-election">Groovy election</a>, a Groovy implementation of a programming challenge</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>You can find all the <a href="http://gradle.org/category/video/">Gradle Summit 2015 videos</a> on the Gradle website</li>
</ul>
<h2 id="mailing-list-posts">Mailing-list posts</h2>
<ul>
<li>A post from Russel Winder on the <a href="http://groovy.329449.n5.nabble.com/What-is-the-status-of-Gpars-tp5727389p5727403.html">status of the GPars project</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>The <a href="https://twitter.com/grooscript/status/633728580309118976">Grooscript project is looking for feedback</a> if you&rsquo;re using it in your company</li>
<li>Happy <a href="https://twitter.com/jfrancoleza/status/636794537751511040">3rd birthday to GrooScript</a>!</li>
<li>Jenn Strater heard of <a href="https://twitter.com/JennStrater/status/637813075370995713">Groovy undergraduate courses</a> but wonders if universities use Groovy at graduate or research levels</li>
<li>Delivering a Groovy course reminds Dierk König how much <a href="https://twitter.com/mittie/status/638659372789465088">he loves the language for its practical nature</a></li>
<li>The IntelliJ IDEA developers are looking for <a href="https://twitter.com/grailsframework/status/638455090068586496">feedback for the Grails 3 support in IDEA</a></li>
<li>Ken Kousen&rsquo;s <a href="https://twitter.com/kenkousen/status/638399868315246593">O&rsquo;Reilly learning path is half the price</a> till tomorrow</li>
<li>The <a href="https://twitter.com/GebFramework/status/633702178813800449">Geb website has been updated with the latest version of Ratpack</a></li>
<li>Jen Stratter shares her <a href="https://twitter.com/JennStrater/status/634718571919220736">ReGinA selfie</a>! Have you shared yours?</li>
<li>Eugene Kamenev&rsquo;s <a href="https://twitter.com/eugenekamenev/status/634613381354401792">OrientDB Groovy entity mapper was added to the OrientDB documentation</a></li>
<li>The <a href="https://twitter.com/golo_lang/status/637171327091253248">Golo programming language is switching to Gradle</a> for its build system</li>
<li>Are you following the <a href="https://twitter.com/Gradlephant/status/634768293673484289">GradlEphant twitter account</a>?</li>
<li>Ted Naleid is working on <a href="https://twitter.com/tednaleid/status/635647401483612160">Spackle, a replacement of the build-test-data Grails plugin</a></li>
<li>Peter Ledbrook reminds that if the new <a href="https://twitter.com/pledbrook/status/638251832721154048">Gradle publishing plugin</a> hasn&rsquo;t worked for you, its source code is available on Github</li>
<li>Lukas Bradley highlighted <a href="https://twitter.com/lukasbradley/status/638368466542555137">Gradle&rsquo;s new hourly 1:1 consulting offer</a></li>
<li>Peter Ledbrook notes that to <a href="https://twitter.com/pledbrook/status/638302250633428993">understand the background of Gradle&rsquo;s new model</a> and an overview of how it works, one should watch Luke Daley&rsquo;s presentation at Gradle Summit</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Peter Ledbrook started a <a href="https://twitter.com/pledbrook/status/636533443980083200">Groovy syntax quick reference guide</a> and is interested in feedback</li>
<li>The August 2015 <a href="http://gradle.org/august-2015-newsletter/">Gradle newsletter</a></li>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="http://grydeske.net/news/show/105">Grails Diary</a> week 33</li>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="http://grydeske.net/news/show/106">Grails Diary</a> week 34</li>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="http://grydeske.net/news/show/107">Grails Diary</a> week 35</li>
<li>A new release of the <a href="http://groovycalamari.com/">Groovy Calamari column</a></li>
<li>Graeme Rocher shares the <a href="https://github.com/grails/grails-core/wiki/Roadmap">Grails 3.1 roadmap</a></li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li><a href="https://twitter.com/gradle/status/634741590771245056">Gradle Inc. is hiring</a> services and R&amp;D engineers</li>
<li><a href="https://twitter.com/jeffscottbrown/status/638430257670828032">OCI is hiring</a> for the Grails team</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Ben Boggess shows how to use <a href="https://twitter.com/benboggess/status/636987319900352512">Groovy&rsquo;s combination() method to create all the combinations of variables in a Spock specification</a></li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>Duncan Dickinson updated his <a href="https://leanpub.com/groovytutorial">Groovy 2 tutorial book</a> on LeanPub</li>
<li>MrHaki published an update of the <a href="http://mrhaki.blogspot.fr/2015/08/groovy-goodness-notebook-is-updated.html">Groovy Goodness Notebook</a></li>
<li>MrHaki published an update to the <a href="http://mrhaki.blogspot.fr/2015/08/gradle-goodness-notebook-updated.html">Gradle Goodness Notebook</a></li>
<li>MrHaki published an update to the <a href="http://mrhaki.blogspot.fr/2015/08/grails-goodness-notebook-updated.html">Grails Goodness Notebook</a></li>
</ul>
<h2 id="podcasts">Podcasts</h2>
<ul>
<li>Audio and video of the <a href="https://twitter.com/groovypodcast/status/634410345814228995">Groovy Podcast episode 18</a> are available</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>The <a href="https://twitter.com/pledbrook/status/637255460643581952">Groovy Grails eXchange Call for Paper</a> is open</li>
<li><a href="https://twitter.com/paulk_asert/status/633959787286233088">Paul King will be speaking at ApacheCon Europe</a> in Budapest about the awesome parts of Groovy</li>
<li><a href="https://twitter.com/restlet/status/634736986272727040">Guillaume Laforge is a featured speaker at JavaOne</a> 2015</li>
<li>Dierk König announces some upcoming <a href="https://twitter.com/mittie/status/638584905694359553">Groovy &amp; Grails courses in Essen</a>, Germany</li>
<li>The <a href="https://twitter.com/farjinaarad/status/633979624733601792">Call for Paper for GrailsConf India</a> 2016 is open</li>
<li>The <a href="https://docs.google.com/forms/d/1ACjCltUodSwZ2u_0X22IeCujV3UGEq3oqp-dMmqmBeo/viewform">Paris Groovy user group is back on September 8th</a> in Paris, to talk about the novelties of Grails 3 and Groovy in the context of telephony and VoIP</li>
<li>An avalanche of SpringOne2GX talks announcements
<ul>
<li>Cédric Champeau will dive into the <a href="https://twitter.com/springone2gx/status/638365711815938051">Groovy compiler internals</a> at SpringOne2GX</li>
<li>Dan Woods will be speaking about <a href="https://twitter.com/danveloper/status/637662873930571776">Ratpack</a></li>
<li>Owen Rubel will speak about <a href="https://twitter.com/owenrubel/status/638771331295760384">scalable APIs with Grails</a></li>
<li>Allison Figus will be speaking about <a href="https://twitter.com/springone2gx/status/638426112394072064">functional automated testing with Geb</a></li>
<li>Michael Plöd will speak about <a href="https://twitter.com/springone2gx/status/638743172806406145">migrating from Grails 2 to 3</a></li>
<li>Guillaume Laforge will be speaking about <a href="https://twitter.com/springone2gx/status/638758243599171584">Groovy and REST</a></li>
<li>Guillaume Laforge will be presenting <a href="https://twitter.com/springone2gx/status/638410980284731392">Groovy with Style</a></li>
<li>Iván López will be speaking about <a href="https://twitter.com/ilopmar/status/638628617740681216">Spock</a></li>
<li>Jorge Franco Leza will be speaking about <a href="https://twitter.com/springone2gx/status/638395906753064961">GrooScript</a></li>
</ul>
</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #75</title><link>https://glaforge.dev/posts/2015/08/15/groovy-weekly-75/</link><pubDate>Sat, 15 Aug 2015 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2015/08/15/groovy-weekly-75/</guid><description>&lt;p>The not-so-weekly Groovy column is back for a summer edition on the shores of the Atlantic ocean (at least, that’s where I am now!) and there was a lot to catch up with in terms of news!&lt;/p>
&lt;p>Ratpack is almost at 1.0, only a couple of weeks to go for the mythical release, whereas Gradle arrived in version 2.6, and some updates to the two main Grails lines were out too.&lt;/p></description><content:encoded>
<![CDATA[<p>The not-so-weekly Groovy column is back for a summer edition on the shores of the Atlantic ocean (at least, that’s where I am now!) and there was a lot to catch up with in terms of news!</p>
<p>Ratpack is almost at 1.0, only a couple of weeks to go for the mythical release, whereas Gradle arrived in version 2.6, and some updates to the two main Grails lines were out too.</p>
<p>You’ll notice lots of delightful content from GR8Conf US 2015! So many talks to watch (and slides to look at), if you didn’t get a chance to attend the conference.</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://ratpack.io/versions/0.9.19">Ratpack 0.9.19</a> released, the last milestone before 1.0!</li>
<li><a href="https://discuss.gradle.org/t/gradle-2-6-released/11092">Gradle 2.6</a> released</li>
<li><a href="https://github.com/grails/grails-core/releases/tag/v3.0.4">Grails 3.0.4</a> released</li>
<li><a href="https://github.com/grails/grails-core/releases/tag/v2.5.1">Grails 2.5.1</a> released</li>
<li><a href="https://twitter.com/GebFramework/status/628475584092377089">Geb 0.12.1</a> released</li>
<li><a href="http://grooscript.org/">GrooScript 1.2.0</a> released</li>
<li><a href="https://twitter.com/grainframework/status/628862549916839936">Grain 0.6.5</a> released with improved Asciidoctor support</li>
<li><a href="https://twitter.com/keeganwitt/status/630918051425353728">Windows installer for Groovy 2.4.4</a> made available by Keegan Witt</li>
<li><a href="https://twitter.com/grooscript/status/631151629845327872">Grails GrooScript 1.1.1</a> plugin released</li>
<li><a href="https://twitter.com/eugenekamenev/status/626623038931488768">OrientDB Groovy 0.1.0</a> released</li>
<li><a href="https://twitter.com/sothmann/status/625756046674497536">TypeScript Gradle plugin 1.5</a> released with all the new TypeScript 1.5 compiler options</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Open-sourcing the <a href="https://engineering.linkedin.com/hadoop/open-sourcing-linkedin-gradle-plugin-and-dsl-apache-hadoop">LinkedIn Gradle plugin and DSL for Apache Hadoop</a> by Alex Bain</li>
<li><a href="https://www.linkedin.com/pulse/groovy-javas-gateway-drug-owen-rubel">Groovy: Java&rsquo;s gateway drug</a>, by Owen Rubel</li>
<li>A <a href="https://www.linkedin.com/pulse/scala-vs-groovy-functional-programming-showdown-owen-rubel">Scala vs Groovy functional programming showdown</a> by Owen Rubel</li>
<li>A <a href="https://github.com/kdabir/awesome-groovy">curated list of awesome Groovy libraries</a>, frameworks, resources, collected by Kunal Dabir</li>
<li><a href="http://gradle.org/maven_vs_gradle/">Feature matrix comparison between Gradle and Maven</a>, on Gradle&rsquo;s site</li>
<li>On InfoQ, Abel Avram writes about <a href="http://www.infoq.com/news/2015/07/gradle-2-5">Gradle 2.5&rsquo;s continuous build</a> feature</li>
<li>Julien Ponge, creator of the Golo dynamic language, <a href="https://julien.ponge.org/blog/an-experiment-in-maven-to-gradle-migration/">compares a non trivial Maven build and a Gradle approach</a></li>
<li><a href="https://www.accelebrate.com/blog/have-a-groovy-spring/">Have a Groovy Spring</a> by Ken Kousen</li>
<li><a href="http://kyleboon.org/blog/2015/08/05/zero-to-ratpack/">Zero to Ratpack</a>, by Kyle Boon, on more concrete use cases &amp; examples for Ratpack</li>
<li><a href="http://kyleboon.org/blog/2015/07/18/stubbing-service-interactions-when-testing-microservices/">Stubbing external service interactions using Ratpack</a> by Kyle Boon</li>
<li><a href="http://www.infoq.com/articles/Ratpack-and-Spring-Boot">Build high performance JVM microservices with Ratpack and Spring Boot</a> by Dan Woods on InfoQ</li>
<li><a href="https://objectpartners.com/2015/08/03/gr8conf-us-2015-conference-recap/">GR8Conf US 2015 recap</a> by Craig Atkinson from Object Partners</li>
<li>Peter Ledbrook on <a href="http://blog.cacoethes.co.uk/groovyandgrails/exploring-grails-3">exploring Grails 3</a></li>
<li><a href="http://jworks.nl/2015/07/31/getting-started-with-asciidoctor/">Getting started with Asciidoctor and Gradle</a> by Erik Pragt</li>
<li><a href="http://www.tothenew.com/blog/grails-filter-at-top-of-filter-invocation-chain/">Grails Filter at top of Filter invocation chain</a> by Sandeep Poonia</li>
<li>Lovin Saini on <a href="http://www.tothenew.com/blog/when-details-in-principal-object-of-spring-security-are-not-sufficient/">when details in principal object of Spring Security in Grails are not sufficient</a></li>
<li><a href="http://www.tothenew.com/blog/integrating-amazon-s3-in-grails-application/">Integrating Amazon S3 in a Grails application</a> by Komal Jain</li>
<li><a href="http://www.tothenew.com/blog/creating-android-application-with-groovy/">Creating Android applications with Groovy</a> by Simranjit Kour</li>
<li><a href="http://www.tothenew.com/blog/closure-delegate-using-groovy-with-method-and-decorating-code-with-multiple-assignments/">Closure delegate using Groovy “with” method and decorating code with multiple assignments</a> by Tarun Pareek</li>
<li><a href="https://dzone.com/articles/upgrading-a-grails-app-from-version-137-to-version">Upgrading a Grails app from version 1.3.7 to 2.4.4</a> by Manvendra Singh on DZone</li>
<li>André Steingreß blogged about <a href="http://blog.andresteingress.com/2015/08/14/grails-hibernate-jdbc-connection/">reconnecting JDBC connections with Grails</a></li>
</ul>
<h2 id="interviews">Interviews</h2>
<ul>
<li>A slice of <a href="http://devops.com/2015/08/13/slice-groovys-hip-use-jfrog-artifactory-bintray-gradle-teamcity/">Groovy&rsquo;s hip use of JFrog&rsquo;s Artifactory &amp; Bintray, and of Gradle &amp; TeamCity</a>, from an interview of Guillaume Laforge on DevOps.com</li>
<li><a href="https://www.voxxed.com/blog/2015/08/ratpack-the-java-8-web-framework-for-independent-thinkers/">Luke Daley interviewed for Voxxed about Ratpack</a>, the “Java 8 web framework for independent thinkers”</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>GR8Conf US 2015
<ul>
<li><a href="https://www.youtube.com/watch?v=Xeh2IExt8rA">Vert.x</a> presented by Ryan Applegate (<a href="https://speakerdeck.com/rappleg/getting-groovy-with-vert-dot-x">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=2tXoPTZmB_4">RESTful Web Services in Grails 3</a> presented by Jenn Strater (<a href="http://jlstrater.github.io/restful-grails3/#/">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=NuL3QkSSXy8">Practical and Stupidly Impractical Groovy DSLs</a> presented by Craig Burke (<a href="http://www.craigburke.com/practical-groovy-dsl/">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=QWnuhlBwNh8">Richer Data History with Event Sourcing</a> presented by Stephen Pember</li>
<li><a href="https://www.youtube.com/watch?v=ugy4wSEy_A0">Groovy Goodness</a>, presented by Hubert Klein Ikkink (<a href="https://github.com/mrhaki/gr8conf2015us/tree/master/groovy-goodness">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=7G6z7Lqxqtw">Groovy DevOps in the Cloud</a> presented by Andrey Adamovich (<a href="http://www.slideshare.net/aestasit/groovy-devops-in-the-cloud-for-gr8conf-us-2015">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=i6WOnN8uJdM">GVM: The Groovy enVironment Manager</a>, presented by Marco Vermeulen (<a href="http://marcovermeulen.github.io/gvm-talk/#/">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=SSK_JaBacE0">Jenkins + Groovy with the Job DSL Plugin</a> presented by Matt Sheehan (<a href="http://sheehan.github.io/job-dsl-slides/">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=iodl0QXkVag">Asset Pipeline</a> presented by David Estes (<a href="https://github.com/GR8conf/GR8ConfUS2015/tree/master/asset-pipeline">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=Ey7sGdmCX2Y">The support tools necessary to effectively build a Microservice Architecture</a> presented by Cameron Fieber from Netflix</li>
<li><a href="https://www.youtube.com/watch?v=yk_VlKUDKMA">Microservices: The Right Way</a> presented by Dan Woods (<a href="http://www.slideshare.net/danveloper/microservices-the-right-way">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=jxGQrJxjF-s">Idiomatic Spock</a> presented by Rob Fletcher (<a href="https://github.com/robfletcher/spock-katas">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=9I8fyX7qnoU">Automated Strategies for deploying Grails from Dev to Prod</a> presented by Eric Helgeson (<a href="https://github.com/erichelgeson/GR8ConfUS2015/blob/585814b5abd383deee512a2f4767d305fa364e18/presentations/Automation%20strategies%20for%20deploying%20your%20Grails%20app%20from%20Dev%20to%20Prod/Automation-Strategies-Eric-Helgeson-gr8-2015.pdf">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=YdrqW22kHS4">Grails Mocking on Steroids</a> presented by Christian Oestreich (<a href="http://grails-plugin-consortium.github.io/mocking-on-steroids-presentation/?full#1">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=rpepX441E5g">Geb Functional Testing Unleashed</a> presented by Craig Atkinson (<a href="http://craigatk.github.io/geb/#/">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=qWPMYysLgis">Nebula: Netflix&rsquo;s OSS Gradle Plugins</a> presented by Rob Spieldenner (<a href="https://speakerdeck.com/rspieldenner/nebula-netflixs-oss-gradle-plugins">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=e5erGrh7n_s">Securing Ratpack</a> presented by Jeff Beck (<a href="http://beckje01.com/talks/gr8us-2015-sec-ratpack.html#/">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=rU0kCAyxSfQ">Cassandra and Grails</a> presented by Jeff Beck (<a href="http://beckje01.com/talks/gr8us-2015-cassandra-grails.html#/">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=onwoLeHRxl0">Spock on Android</a> by Andrew Reitz (<a href="http://andrewreitz.com/gr8conf-us-spock-android-2015/#/">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=ymbvs4qoQJU">Groovy and Scala: Friends or Foes</a> presented by Marco Vermeulen (<a href="http://marcovermeulen.github.io/groovy-and-scala-talk/#/">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=U_p2L47gvgk">Getting Groovy on Android</a> presented by Andrew Reitz (<a href="https://onedrive.live.com/view.aspx?resid=29181012CB08492E!882&amp;ithint=file%2cpptx&amp;app=PowerPoint&amp;authkey=!AGwH3YhuhRS1P2s">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=vY5mYcw3XHk">Source to Deployment with Gradle and Docker</a> presented by John Engelman (<a href="https://speakerdeck.com/johnrengelman/source-to-deployment-with-gradle-and-docker">slides</a>)</li>
<li><a href="https://www.youtube.com/watch?v=-49ng22DHkw">Learn how to get Groovy on Google Glass and Android Wear</a> presented by Ryan Vanderwerf</li>
<li><a href="https://www.youtube.com/watch?v=NBv5y2O1oTY">Little Did He Know&hellip;</a> presented by Burt Beckwith</li>
<li><a href="https://www.youtube.com/watch?v=_FFpngCk52c">Gradle: State of the Build</a> presented by Luke Daley</li>
<li><a href="https://www.youtube.com/watch?v=4-f0vEWQ9Os">Hacking the Grails Spring Security 2.0 Plugin</a>, presented by Burt Beckwith</li>
<li><a href="https://www.youtube.com/watch?v=WT8S-dMLSVM">Ratpack: Under the Hood</a> presented by Luke Daley (<a href="https://github.com/ratpack/presentation-ratpack-under-the-hood-gr8conf-us-2015">slides</a>)</li>
<li>Slides on <a href="https://github.com/jondejong/GR8confUS-Grooscript">GrooScript</a> by Jon DeJong</li>
<li>Slides on <a href="http://www.slideshare.net/StevePember/gr8conf-us-2015-intro-to-event-sourcing-with-groovy">introduction to event sourcing</a> by Steve Pember</li>
<li>Slides on <a href="https://speakerdeck.com/johnrengelman/java-8-functional-programming-for-groovy-developers">Java 8 functional programming for Groovy developers</a> by John Engelman</li>
<li>Slides on <a href="http://www.slideshare.net/StevePember/gr8conf-us-2015-reactive-options-for-groovy">reactive options for Groovy</a> by Steve Pember</li>
<li>Slides on <a href="http://www.craigburke.com/angular-groovy-world/">Angular.JS in a Groovy world</a> by Craig Burke</li>
<li>Slides on <a href="http://www.slideshare.net/aestasit/infrastructure-automation-with-gradle-and-puppet-for-gr8conf-us-2015">infrastructure automation with Gradle and Puppet</a> by Andrey Adamovitch</li>
</ul>
</li>
<li><a href="http://www.infoq.com/presentations/eclipse-gradle">Eclipse &amp; Gradle, the best of both worlds</a>, at EclipseCon, by Hans Dockter and Etienne Studer</li>
<li>Dan Woods <a href="http://fr.slideshare.net/danveloper/ratpack-web-framework">Ratpack web framework</a> presentation at UberConf</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Keegan Witt morphed a <a href="https://twitter.com/keeganwitt/status/613718739217948672">3 hour running Bash script of HDFS commands into a 10 seconds Groovy script</a> using the Java APIs</li>
<li>The <a href="https://twitter.com/danveloper/status/626768428498579456">killer app for Groovy is Gradle</a>, says Ken Kousen</li>
<li>Erik Pragt spotted a <a href="https://twitter.com/epragt/status/631440665981923328">nice Groovy in Action, 2nd edition, review</a> by Andrew Binstock, on Oracle&rsquo;s Java Magazine</li>
<li><a href="https://twitter.com/ratpackweb/status/631781599621677056">Redis session storage coming to Ratpack</a> courtesy of Jeff Beck</li>
<li><a href="https://twitter.com/sdkmanager/status/630732523706949632">Gradle 2.6</a> available on GVM</li>
<li><a href="https://twitter.com/grooscript/status/630094999091343360">GrooScript&rsquo;s source code evolution</a> video</li>
<li>A video of a <a href="https://twitter.com/dhirajmahapatro/status/630030753758343168">Groovy ecosystem variant of the 2048</a> game</li>
<li>Iván López is working on a <a href="https://twitter.com/ilopmar/status/627801822347022336">Grails framework postgres-extensions plugin</a></li>
<li>Dan Woods learned that <a href="https://twitter.com/danveloper/status/627350450870820864">Bintray is using Ratpack</a></li>
<li>Marco Vermeulen is excited with the <a href="https://twitter.com/marc0der/status/627264041677901824">Gradle continuous build feature</a></li>
<li><a href="https://twitter.com/monzdrpower/status/627150274453487616">Another Groovy in Action 2nd edition picture</a>, from Epam Systems in Saint-Petersburg</li>
<li><a href="https://twitter.com/javazquez/status/627147102779367424">GVM is going to be renamed SDKman</a></li>
<li>Robert Fletcher discovered <a href="https://twitter.com/rfletcherEW/status/626889088738664449">Spock&rsquo;s @Use annotation</a> for using extension methods in tests</li>
<li>MrHaki shares a link to a <a href="http://asciidoctor.github.io/asciidoctor-gradle-examples/#_livereload_html_example">LiveReload integration with Gradle</a></li>
<li>Colin Harrington shares the ingredients of an <a href="https://twitter.com/ColinHarrington/status/626784519475965952">awesome sauce for Android development</a>: SwissKnife + Android Spock + Grooid tools</li>
<li><a href="https://twitter.com/sdkmanager/status/626321911056887808">Grails 3.0.4</a> available on GVM</li>
<li><a href="https://twitter.com/sdkmanager/status/626361104114053120">Grails 2.5.1</a> available on GVM</li>
<li>Burt Beckwith uploaded the <a href="https://twitter.com/burtbeckwith/status/632590650442129408">documentation for the Grails Spring Security Core plugin</a> v3.0.0.M1</li>
<li>Victor Mrtez believes Groovy rocks and is going to code another <a href="https://twitter.com/AnInfinite/status/626444888452808704">Jenkins CI plugin with Groovy and Gradle</a></li>
<li>At <a href="https://twitter.com/rfletcherEW/status/626599837421686784">GR8Conf US 2015, the Ratpack team gathered</a> to drink Sinatra&rsquo;s favorite drink!</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li><a href="https://twitter.com/glaforge/status/632669415876763648">Keegan Witt joined the Groovy team</a> as a committer</li>
<li>Do you want a <a href="http://gradle.org/gradle-com-sneak-peek/">Gradle.com sneak peek</a>?</li>
<li>Jacob Aae Mikkelsen’s <a href="http://grydeske.net/news/show/102">Grails Diary</a> week 30</li>
<li>Jacob Aae Mikkelsen’s <a href="http://grydeske.net/news/show/103">Grails Diary</a> week 31</li>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="http://grydeske.net/news/show/104">Grails Diary</a> week 32</li>
<li>The 11th edition of the weekly <a href="http://groovycalamari.com/">Groovy Calamari</a></li>
<li>Marco Vermeulen has started a <a href="https://github.com/marcoVermeulen/groovy-scala-extension-module">Groovy extension module for Scala interop</a></li>
<li>The <a href="https://github.com/groovy/scriptom">Scriptom</a> project moved to the Groovy Github organization</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>A <a href="https://github.com/bertramdev/gSTOMP-client">Groovy STOMP client</a> by David Estes</li>
<li>Russell Hart&rsquo;s <a href="https://github.com/ratpack/hands-on-ratpack">Ratpack workshop code base</a></li>
<li>Kyle Boon <a href="https://twitter.com/kyleboon/status/626169879444504576">open sourced his Cellar HQ Ratpack application</a></li>
<li><a href="https://gist.github.com/danhyun/980319864f75967ccd8a">Utilize Gradle&rsquo;s idea plugin to set git as your default VCS</a> and to enable the Gradle tool from IntelliJ IDEA, by Danny Hyun</li>
<li>Václav Pech <a href="http://www.jroller.com/vaclav/entry/wagons_counted">counts wagons in Groovy</a> in this algorithmic teaser</li>
<li>Circle Stacker <a href="https://github.com/pieces029/circle-stacker">demo app using Groovy, Swiss Knife</a>, and built with Gradle</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li><a href="https://twitter.com/werner_cjd/status/632362950200819712">Free Gradle books</a> from O&rsquo;Reilly</li>
<li>Dierk König says that <a href="https://twitter.com/mittie/status/625320234778525696">Groovy in Action, 2nd edition, was #5 in Manning print book bestsellers</a> a month after appearing</li>
</ul>
<h2 id="podcasts">Podcasts</h2>
<ul>
<li><a href="https://www.youtube.com/watch?v=ii693nUQwV0">Groovy Podcast episode 17</a>, recorded at GR8Conf US 2015</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="http://www.meetup.com/Austin-Groovy-and-Grails-Users/events/224611516/">Groovy 2 and Java 8, gotchas &amp; future</a>, at the Groovy user group in Austin on August 27th</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #74</title><link>https://glaforge.dev/posts/2015/07/21/groovy-weekly-74/</link><pubDate>Tue, 21 Jul 2015 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2015/07/21/groovy-weekly-74/</guid><description>&lt;p>Although on a more irregular schedule, Groovy Weekly continues in the summer with its 74th edition, crossing the bar of the 3000 total news items tracked since it all started!&lt;/p>
&lt;p>Some interesting releases for this edition, like the &lt;a href="http://groovy-lang.org/changelogs/changelog-2.4.4.html">Groovy 2.4.4&lt;/a> release under the Apache umbrella! After a failed attempt, our second try was succesful, and it was particularly important to get it right out the door, as an &lt;a href="http://groovy-lang.org/security.html">important security issue&lt;/a> uncovered was fixed in this release, and all Groovy users (and frameworks and libraries using Groovy) should upgrade as soon as possible — especially as no older version will be released with the patch.&lt;/p></description><content:encoded>
<![CDATA[<p>Although on a more irregular schedule, Groovy Weekly continues in the summer with its 74th edition, crossing the bar of the 3000 total news items tracked since it all started!</p>
<p>Some interesting releases for this edition, like the <a href="http://groovy-lang.org/changelogs/changelog-2.4.4.html">Groovy 2.4.4</a> release under the Apache umbrella! After a failed attempt, our second try was succesful, and it was particularly important to get it right out the door, as an <a href="http://groovy-lang.org/security.html">important security issue</a> uncovered was fixed in this release, and all Groovy users (and frameworks and libraries using Groovy) should upgrade as soon as possible — especially as no older version will be released with the patch.</p>
<p>In the release section, last time, I had forgotten <a href="http://griffon-framework.org/releasenotes/griffon_2.3.0.html">Griffon 2.3.0</a> which was released on stage at GR8Conf Europe by Andrés Almiray. And in other releases, we also have <a href="https://twitter.com/grailsframework/status/619082386320572416">Grails 3.0.3</a> with important reloading improvements, <a href="https://groups.google.com/forum/#!topic/geb-user/2LXtg0pP4Do">Geb 0.12.0</a> which celebrated its 5th anniversary, and also <a href="https://github.com/CodeNarc/CodeNarc/blob/master/CHANGELOG.txt">Codenarc 0.24</a> that I had forgotten the previous edition as well.</p>
<p>It looks like Slack is really becoming very popular as both <a href="https://twitter.com/grailsframework/status/621780975870152704">Grails</a> and <a href="https://twitter.com/ratpackweb/status/621103100619681792">Ratpack</a> opened Slack channels!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://groovy-lang.org/changelogs/changelog-2.4.4.html">Groovy 2.4.4</a> released its first version under the Apache umbrella</li>
<li><a href="http://griffon-framework.org/releasenotes/griffon_2.3.0.html">Griffon 2.3.0</a> released</li>
<li><a href="https://twitter.com/grailsframework/status/619082386320572416">Grails 3.0.3</a> released with improvements to reloading</li>
<li><a href="https://groups.google.com/forum/#!topic/geb-user/2LXtg0pP4Do">Geb 0.12.0</a> released</li>
<li><a href="https://github.com/CodeNarc/CodeNarc/blob/master/CHANGELOG.txt">Codenarc 0.24</a> released</li>
<li><a href="https://twitter.com/CedricChampeau/status/623453951116070912">Deck2PDF 0.3</a> released by Cédric Champeau allowing HTML/JavaScript presentations to be saved as PDF or images</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>JaxEnter covers the <a href="https://jaxenter.com/groovy-2-4-4-has-landed-under-the-apache-foundation-119018.html">Groovy 2.4.4 release</a> under the Apache umbrella</li>
<li>Ryan Vanderwerf on <a href="http://rvanderwerf.blogspot.fr/2015/07/how-to-publish-grails-3-plugin.html">publishing Grails 3 plugin snapshots to your local Artifactory</a> server</li>
<li><a href="http://beckje01.com/blog/2015/07/12/grails-3-and-jacoco/">Grails 3 and JaCoCo</a> by Jeff Beck</li>
<li>Graeme Rocher announces the <a href="http://grails.io/post/124146177848/join-grails-community-on-slack">creation of a Grails Slack channel</a></li>
<li>First look at the new <a href="http://inthecheesefactory.com/blog/new-gradle-build-tools-with-gradle-2.5/en">Android Gradle build tools</a>: the new DSL structure and Gradle 2.5</li>
<li>Benjamin Muschko&rsquo;s Gradle in the trenches on <a href="https://gradle.org/tales-from-the-trenches-defining-versioning-strategies/">defining versioning strategies</a></li>
<li><a href="http://trickyandroid.com/gradle-tip-3-tasks-ordering/">Gradle task ordering tip</a> by Pavel Dudka</li>
<li><a href="https://rdmueller.github.io/Spock-Reports-with-Grails-3.0/">Spock-Reports plugin for Grails 3</a> by R.D. Müller</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Bogdan Danilyuk presented on <a href="https://vimeo.com/131395565">modularity with Grails</a> at GeekOut from real-life usage at TransferWise</li>
<li><a href="https://speakerdeck.com/bmuschko/advanced-dependency-management-with-gradle">Advanced dependency management with Gradle</a> by Benjamin Muschko</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Original Groovy co-founder <a href="https://twitter.com/jstrachan/status/622020452886663168">James Strachan states that Groovy is the language of Continuous Integration and Continuous Delivery</a>, and enjoys using it with Jenkins Flow and Docker workflows</li>
<li><a href="https://twitter.com/sdkmanager/status/621639983217119232">Groovy 2.4.4</a> available on GVM</li>
<li>Gradle 2.6 will be featuring a <a href="https://twitter.com/gradle/status/621458781273128960">test kit for functionally testing Gradle plugins</a></li>
<li>Andrés Almiray developed two <a href="https://twitter.com/aalmiray/status/621059717557821441">new Griffon AST transformations targeting JavaFX</a> with @ListChangeListener and @MapChangeListener</li>
<li><a href="https://twitter.com/GebFramework/status/619234577849028608">5th anniversary of Geb</a></li>
<li>Jennifer Strater has a <a href="https://twitter.com/JennStrater/status/620692583182790656">visualization of gender ratios in the Groovy community</a></li>
<li>Vladimír Oraný is doing heavy <a href="https://twitter.com/musketyr/status/621969659982647296">refactorings with confidence thanks to the Spock framework test coverage</a></li>
<li>A <a href="https://twitter.com/alvaro_sanchez/status/623477231050862592">Groovy Grape tour bus</a> in Australia spotted by Álvaro Sánchez!</li>
<li>Ryan Vanderwerf forked <a href="https://twitter.com/RyanVanderwerf/status/622978045490507780">grooid-templates to create a ready-made Glass or Android Wear app</a> template</li>
<li>Mario García made a new version of <a href="https://twitter.com/marioggar/status/621943669550149632">Grooid-templates with Spock framework support</a> for testing</li>
<li>A <a href="https://plugins.gradle.org/plugin/com.github.pedjak.dockerized-test/0.3.3">Gradle plugin for Dockerized tests</a></li>
<li><a href="https://twitter.com/sdkmanager/status/623355138699317248">GVM adds JBoss Forge</a> as new candidate</li>
<li><a href="https://twitter.com/sdkmanager/status/619076452978442240">Grails 3.0.3</a> available on GVM</li>
<li><a href="https://twitter.com/sdkmanager/status/619053543048278016">Vert.x 3.0</a> available on GVM</li>
<li><a href="https://twitter.com/sdkmanager/status/619050062354771968">Gradle 2.5</a> available on GVM</li>
<li>The <a href="https://twitter.com/djensen47/status/622090181861113856">Gradle dexinfo plugin</a> adds a new task to print out the dex method count of your Android projects without having to install separate tools</li>
<li>Danny Hyun reminds us about the <a href="https://twitter.com/Lspacewalker/status/621120598446575616">Ratpack Slack channel</a></li>
<li>You can <a href="https://twitter.com/ratpackweb/status/621103100619681792">sign-up for the Ratpack Slack channel</a></li>
<li>300 persons have joined the <a href="https://twitter.com/grailsframework/status/621780975870152704">Grails Slack channel</a></li>
<li>Graeme Rocher announces that <a href="https://twitter.com/graemerocher/status/620944698140704768">dynamic scaffolding will make a come back in the next Grails 3 release</a></li>
<li>Claus Ibsen <a href="https://twitter.com/davsclaus/status/621668001050415104">upgraded Apache Camel to Groovy 2.4.4</a> right after its release under the Apache flag</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Groovy 2.4.3 and below was affected by a <a href="http://groovy-lang.org/security.html">security issue</a> that has been fixed in Groovy 2.4.4. Projects should upgrade to 2.4.4 as soon as possible to avoid this security issue to be leveraged by malicious attackers.</li>
<li>Jacob Aae Mikkelsen <a href="http://grydeske.net/news/show/101">Grails Diary</a> week 25 to 28</li>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="http://grydeske.net/news/show/101">Grails Diary</a> week 29</li>
<li>Mockito is bringing some <a href="https://github.com/mockito/mockito/pull/266">better support for Groovy classes</a></li>
</ul>
<h2 id="screencasts">Screencasts</h2>
<ul>
<li>Bertrand Goetzmann recorded a video of a <a href="https://twitter.com/bgoetzmann/status/623237871865040901">lunar lander game he developed with GroovyFX</a>, JavaFX and a bit of reactive programming</li>
</ul>
<h2 id="podcasts">Podcasts</h2>
<ul>
<li>Audio of <a href="https://twitter.com/groovypodcast/status/620708302142459904">Groovy podcast</a> episode 15</li>
<li><a href="https://plus.google.com/events/cs8io51q6nqdd0iafel0s0p0pjs">Groovy Podcast</a> episode 16</li>
</ul>
<h2 id="mailing-list-posts">Mailing-list posts</h2>
<ul>
<li><a href="http://mail-archives.apache.org/mod_mbox/incubator-groovy-users/201507.mbox/%3CCADQzvmk=3TwWzeGMN-HXwv4CUkbPBENusioviPf29_uJ=zLi0w@mail.gmail.com%3E">Groovy 2.4.4 release announcement</a> on the Groovy mailing-list by Cédric Champeau</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #73</title><link>https://glaforge.dev/posts/2015/07/08/groovy-weekly-73/</link><pubDate>Wed, 08 Jul 2015 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2015/07/08/groovy-weekly-73/</guid><description>&lt;p>It’s definitely not Tuesday, but nonetheless, lots of accumulated great and Groovy content to share with you all! It’s not easy to keep up the pace, sorry for the irregular timing of this column — I might have to remove the “Weekly” part of the name! But you won’t regret this edition, packed with so much content!&lt;/p>
&lt;p>Already one month gone by since GR8Conf Europe 2015, but we have content to share about the event! And GR8Conf US 2015 is happening soon too! We also have presentations and videos from Gradle Summit and Greach 2015.&lt;/p></description><content:encoded>
<![CDATA[<p>It’s definitely not Tuesday, but nonetheless, lots of accumulated great and Groovy content to share with you all! It’s not easy to keep up the pace, sorry for the irregular timing of this column — I might have to remove the “Weekly” part of the name! But you won’t regret this edition, packed with so much content!</p>
<p>Already one month gone by since GR8Conf Europe 2015, but we have content to share about the event! And GR8Conf US 2015 is happening soon too! We also have presentations and videos from Gradle Summit and Greach 2015.</p>
<p>Guillaume Laforge tweeted about the recent download numbers of the Groovy project: for the first 6 monts of the year, Groovy was downloaded 4.5 million times (1 million through Bintray and 3.5 millions through Maven Central). In 6 months, Groovy was downloaded as much as the full year of 2014!</p>
<p>Another big milestone is the release in print of the second edition of Groovy in Action! Lots of fans tweeted their “ReGinA selfies!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://ratpack.io/versions/0.9.18">Ratpack 0.9.18</a> released</li>
<li><a href="https://groups.google.com/forum/?fromgroups=#!topic/vertx/xgGgQcDeX04">Vert.x 3.0</a> released</li>
<li><a href="https://discuss.gradle.org/t/gradle-2-5-released/10490">Gradle 2.5</a> released with continuous build and dependency substitution rules</li>
<li><a href="https://twitter.com/grailsframework/status/610790605980045312">Grails 3.0.2</a> released with lots of improvements, and the first release under OCI&rsquo;s leadership</li>
<li><a href="https://twitter.com/grooscript/status/617354776897056768">GrooScript 1.1.2</a> released</li>
<li><a href="https://twitter.com/grooscript/status/614796850395283456">GrooScript Grails 3 plugin</a> released in version 1.0.0</li>
<li><a href="https://twitter.com/sdkmanager/status/611213808569335808">GVM 2.4.2</a> released with key security patches</li>
<li><a href="https://twitter.com/ysb33r/status/613705160238370818">Groovy VFS 1.0-beta-3</a> released</li>
<li><a href="https://twitter.com/ysb33r/status/613711977240657927">Groovy VFS command-line 1.0-beta-3</a> released</li>
<li><a href="https://marketplace.eclipse.org/content/buildship-gradle-integration">Buildship Gradle plugin support</a> released</li>
<li><a href="http://glu.977617.n3.nabble.com/ANN-glu-5-6-1-released-td4026928.html">Glu 5.6.1</a> released</li>
<li><a href="http://spring.io/blog/2015/07/02/spring-boot-1-2-5-released">Spring Boot 1.2.5</a> released</li>
<li><a href="http://gradle.org/release-candidate/">Gradle 2.5-rc-2</a> released</li>
<li><a href="https://twitter.com/grainframework/status/613008570691420160">Grain 0.6.4</a> released with updated Asciidoctor support for include macros</li>
<li><a href="https://twitter.com/SonarSource/status/610725375400935424">SonarQube Gradle 1.0</a> makes it easier to cover more versions of both Gradle and Sonar</li>
<li><a href="https://twitter.com/pickypg/status/610534606425735168">ElasticSearch Groovy client 1.6.0</a> released with improved closure conversion</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Graeme Rocher sums up his <a href="http://grails.io/post/120598713483/grails-at-oci-week-1">first week at his new job at OCI</a></li>
<li><a href="http://www.aurorasolutions.io/blog/grails-spring-security-filter-2fa-yubico-example/">Grails Spring Security filter for professional 2 factor authentication</a> with Yubico</li>
<li>Pavel Dudka shares a tip on <a href="http://trickyandroid.com/gradle-tip-2-understanding-syntax/">understanding the Groovy syntax for working with Gradle</a></li>
<li><a href="http://www.tothenew.com/blog/day-1-gr8conf-eu-2015/">Day one coverage of GR8Conf Europe</a> 2015 by To The New Digital</li>
<li><a href="http://www.tothenew.com/blog/day-2-gr8conf-eu-2015/">Day two coverage of GR8Conf Europe</a> 2015 by To The New Digital</li>
<li><a href="http://nemerosa.ghost.io/2015/07/01/publishing-to-the-maven-central-using-gradle/">Publishing to Maven Central using Gradle</a> by Damien Coraboeuf</li>
<li>Petri Kainulainen helps you <a href="http://www.petrikainulainen.net/getting-started-with-gradle/">getting started with Gradle</a></li>
<li><a href="https://twitter.com/gradleplugins/status/613070984782999553">Sync content on Amazon S3 with Gradle</a></li>
<li>Ryan Harter on <a href="http://ryanharter.com/blog/2015/06/18/hosting-a-private-maven-repo-on-amazon-s3/">hosting a private Maven repository on Amazon S3</a></li>
<li>Anto Aravinth published a <a href="https://twitter.com/antoaravinth/status/612112565137571840">GrooScript plugin for Karma</a></li>
<li>Running <a href="http://www.mkyong.com/spring-mvc/gradle-spring-4-mvc-hello-world-example-annotation/">Spring MVC 4 with Gradle and Gretty</a></li>
<li><a href="https://objectpartners.com/2015/06/10/websockets-in-grails-3-0/">WebSockets in Grails 3.0</a> by Mike Plummer</li>
<li><a href="https://blog.nraboy.com/2015/04/using-gradle-in-your-command-line-android-project/">Using Gradle in your command-line Android projects</a> by Nic Raboy</li>
<li>Iván López about <a href="http://www.kaleidos.net/blog/1168/speaking-at-gr8conf/">speaking at GR8Conf</a> Europe 2015</li>
<li>Painless Android development with Groovy and SwissKnife
<ul>
<li><a href="http://www.codebulb.ch/2015/06/painless-android-development-with-groovy-and-swissknife-part-1.html">part one</a></li>
<li><a href="http://www.codebulb.ch/2015/06/painless-android-development-with-groovy-and-swissknife-part-2.html">part two</a></li>
<li><a href="http://www.codebulb.ch/2015/06/painless-android-development-with-groovy-and-swissknife-part-3.html">part three</a></li>
</ul>
</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>GR8Conf Europe 2015
<ul>
<li>The <a href="https://twitter.com/gr8conf/status/605991341676494848">GR8Conf keynote</a> by Guillaume Laforge was livestreamed on YouTube</li>
<li>The slides of the <a href="https://speakerdeck.com/glaforge/groovy-state-of-the-union-gr8conf-europe-2015">Groovy keynote</a> by Guillaume Laforge</li>
<li><a href="https://speakerdeck.com/glaforge/groovy-with-style-gr8conf-europe-2015">Groovy with style</a> by Guillaume Laforge</li>
<li><a href="http://danhyun.github.io/gr8conf-eu-2015-contribute-to-gr8tech/#/">Contributing to GR8 technologies</a> by Danny Hyun</li>
<li><a href="http://danhyun.github.io/gr8conf-eu-2015-groovy-safari/#/">Groovy Safari</a> by Danny Hyun, a beginner workshop</li>
<li><a href="http://fr.slideshare.net/alvarosanchezmariscal/ratpack-101-gr8conf-2015">Ratpack 101 workshop</a> by Álvaro Sánchez Mariscal</li>
<li><a href="http://jlstrater.github.io/No-Nonsense-NoSQL/#/">No-nonsense NoSQL</a> by Jennifer Strater</li>
<li><a href="https://speakerdeck.com/bsideup/gr8conf-groovy-under-macroscope">Groovy under Macro-scope</a> by Sergei Egorov</li>
<li><a href="http://fr.slideshare.net/ilopmar/gr8conf-2015-spring-boot-and-groovy-what-more-do-you-need">Spring Boot and Groovy</a> by Iván López</li>
<li>Göran Ehrsson on <a href="http://gr8crm.github.io/">customer relationship management plugins for Grails</a></li>
<li><a href="https://speakerdeck.com/glaforge/groovy-rest-gr8conf-europe-2015">Groovy REST</a> by Guillaume Laforge</li>
<li><a href="http://fr.slideshare.net/ryanvanderwerf/all-your-legos-are-belong-to-us-testout">Groovy and legos</a> by Ryan Wanderverf</li>
<li><a href="http://imada.sdu.dk/~jamik/gr8conf/geb.html#/">Geb for testing Grails applications</a> by Jacob Aae Mikkelsen</li>
<li><a href="http://imada.sdu.dk/~jamik/gr8conf/lessons-learned.html#/">Lessons learned teaching a Groovy / Grails course</a> by Jacob Aae Mikkelsen</li>
<li><a href="http://fr.slideshare.net/ilopmar/gr8conf-2015-testing-with-spock-the-logical-choice">Testing with Spock, the logical choice</a>, by Iván López</li>
<li><a href="http://fr.slideshare.net/sascha_klein/groovy-ontheshell-gr8-48989676">Groovy on the Shell</a> by Alexander Klein</li>
<li><a href="http://fr.slideshare.net/sascha_klein/android-on-groovygr8-48989809">Android on Groovy</a> by Alexander Klein</li>
<li><a href="http://fr.slideshare.net/alvarosanchezmariscal/stateless-authentication-for-microservices-gr8conf-2015">Stateless authentication for microservices</a> by Álvaro Sánchez Mariscal</li>
<li><a href="http://es.slideshare.net/JorgeFrancoLeza/grooscript-gr8conf-2015">GrooScript in Action</a> by Jorge Franco Leza</li>
<li><a href="http://fr.slideshare.net/aestasit/rapid-web-application-development-with-groovy-ratpack-for">Rapid application development with Ratpack</a> by Andrey Adamovich</li>
<li>Guillaume Laforge presented on <a href="https://glaforge.dev/talks/2015/06/24/groovy-on-android-for-the-paris-android-user-group/">Groovy on Android</a> at the Paris Android User Group</li>
<li><a href="http://zeroturnaround.com/rebellabs/gradle-hot-or-not-by-andres-almiray/">Gradle hot or not</a>, by Andrés Almiray</li>
<li>Iván López&rsquo; slides and code on <a href="https://twitter.com/ilopmar/status/618681788307800064">metaprogramming options with Groovy</a></li>
</ul>
</li>
<li>Greach 2015 videos
<ul>
<li>Andrés Almiray presented the <a href="https://www.youtube.com/watch?v=gmDi-8k2NhQ">Groovy Ecosystem</a></li>
<li><a href="http://greachconf.com/speakers/steve-pember-groovy-options-for-reactive-programming/">Groovy options for reactive programming</a> by Steve Pember</li>
<li><a href="http://greachconf.com/speakers/russel-winder-gpars-remoting/">GPars remoting</a> by Russel Winder</li>
<li><a href="http://greachconf.com/speakers/rene-groeschke-beyond-gradle-2-0/">Beyond Gradle 2</a> by René Gröschke</li>
<li><a href="http://greachconf.com/speakers/schalk-w-cronje-idiomatic-gradle-plugin-writing/">Idiomatic Gradle plugin writing</a> by Schalk Cronjé</li>
<li><a href="http://greachconf.com/speakers/steve-pember-advanced-microservice-concerns/">Advanced micro services concerns</a> by Steve Pember at Greach 2015</li>
</ul>
</li>
<li>Gradle Summit presentations
<ul>
<li><a href="https://speakerdeck.com/bmuschko/state-of-the-art-gradle-multi-module-builds">State of the art Gradle multi-module builds</a> by Benjamin Muschko at Gradle Summit</li>
<li><a href="https://speakerdeck.com/bmuschko/advanced-dependency-management-with-gradle">Advanced dependency management with Gradle</a> by Benjamin Muschko at Gradle Summit</li>
<li><a href="http://fr.slideshare.net/jmcgarr/centralized-team-in-a-decentralized-world-engineering-tools-at-netflix">A centralized team in a decentralized world</a> Mike McGarr at Gradle Summit</li>
</ul>
</li>
</ul>
<h2 id="interviews">Interviews</h2>
<ul>
<li>Andrés Almiray interviewed at Greach about the <a href="https://www.youtube.com/watch?v=OtzQYJxHcaA">Griffon ecosystem</a> (in Spanish but subtitled in English)</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Guillaume Laforge announces that <a href="https://twitter.com/glaforge/status/617061188199936000">Groovy has been downloaded 4.5 million times for the first half of the year</a>, as much as the whole of 2014. With 1M downloads from Bintray and 4.5M downloads from Maven Central.</li>
<li>Guillaume Laforge updated the <a href="https://twitter.com/glaforge/status/617443128988028928">Groovy Web Console to the latest Groovy 2.4.3</a>, developed with the latest Gaelyk 2.1.2 update</li>
<li>Graeme Rocher <a href="https://twitter.com/graemerocher/status/616903346499272704">reworked the reloading in Grails 3.0.3</a></li>
<li>Søren Berg Glasius and Guillaume Laforge are launched <a href="https://twitter.com/glaforge/status/605992876753985536">the 7th edition of GR8Conf Europe 2015</a>!</li>
<li>Ixchel Ruiz says <a href="https://twitter.com/ixchelruiz/status/606002309349416960">Groovy is alive and thriving</a>!</li>
<li>Schalk Cronjé drew his <a href="https://twitter.com/ysb33r/status/606009047607865344">summary of Guillaume Laforge&rsquo;s GR8Conf keynote</a></li>
<li>Kostis Kapelonis feels that <a href="https://twitter.com/codepipes/status/617355655352057858">even after the release of Java 8, Groovy closures are still more convenient</a></li>
<li>Seems like <a href="https://twitter.com/DexafreeHTC/status/616640764173512704">developing on Android with Groovy and RxJava can blow your mind</a>!</li>
<li>Dierk König shares interesting <a href="https://twitter.com/mittie/status/616613035126484992">ranking information about Groovy in Action on Amazon</a></li>
<li>ReGinA selfies:
<ul>
<li><a href="https://twitter.com/sebi2706/status/617003864068386816">Sébastien Blanc</a></li>
<li><a href="https://twitter.com/fifthposition/status/615593893082603521">Ben Klein</a></li>
<li><a href="https://twitter.com/rfletcherEW/status/611990264698114048">Robert Fletcher</a></li>
<li><a href="https://twitter.com/glaforge/status/611072178407219201">Guillaume Laforge</a></li>
</ul>
</li>
<li>Russell Hart says that <a href="https://twitter.com/rus_hart/status/616586423102218240">GroovyChainAction is back in Ratpack 0.9.18</a></li>
<li>Russell Hart encourages users to <a href="https://twitter.com/rus_hart/status/616584233059000320">seek for help on the Ratpack forum</a></li>
<li><a href="https://twitter.com/sdkmanager/status/616505672537083905">Spring Boot 1.2.5</a> available on GVM</li>
<li>If you&rsquo;re <a href="https://twitter.com/RebelLabs/status/615963195006656513">missing Maven archetypes in Gradle, try Lazybones</a>, says Andrés Almiray</li>
<li>The <a href="https://twitter.com/GebFramework/status/615487750037483520">Geb framework team has caught up with the outstanding pull requests</a>, time to submit some more!</li>
<li>Andrés Almiray claims that <a href="https://twitter.com/aalmiray/status/615275454497452032">migration from Maven to Gradle is a matter of &ldquo;when&rdquo;, not of &ldquo;if&rdquo;</a></li>
<li>Tim Yates loves how <a href="https://twitter.com/tim_yates/status/615261252328292352">Spock makes mocking and interaction testing easy and readable</a> (with a code sample)</li>
<li>The <a href="https://twitter.com/GebFramework/status/614407603620327424">book of Geb has migrated to Asciidoctor with executable code samples</a></li>
<li>Danny Hyun encourages folks to use the <a href="https://twitter.com/Lspacewalker/status/614148984832139265">asset pipeline Gradle plugin</a></li>
<li>Peter Ledbrook points at the <a href="https://twitter.com/pledbrook/status/613998420404125696">Gradle continuous build feature that responds to filesystem changes</a> by running tests again</li>
<li><a href="https://twitter.com/sdkmanager/status/613992559900295168">Gradle 2.5-rc-1</a> available on GVM</li>
<li>Kevin Tan pointed me at <a href="https://twitter.com/S1lv3rd3m0n/status/613937882605027328">another Groovy-built Android application available in the Play store</a></li>
<li>Ratpack.io is now powered by the <a href="https://twitter.com/Lspacewalker/status/613101435270643716">Ratpack asset pipeline plugin</a></li>
<li><a href="https://twitter.com/sdkmanager/status/612555268677836800">Legacy versions of Groovy are restored on GVM</a>, thanks to the Bintray team, Marco Vermeulen and Guillaume Laforge</li>
<li><a href="https://twitter.com/danveloper/status/611604849508151296">AngularJS Annotate is a Gradle Asset Pipeline module</a> that allows AngularJS code to be minified</li>
<li>Cédric Champeau announces <a href="https://twitter.com/CedricChampeau/status/611332345707139074">older versions of Groovy to be available on Bintray</a></li>
<li>Say hello to <a href="https://twitter.com/davydotcom/status/611212285382664193">automatic relative asset url replacement in static HTML</a> with the Gradle asset pipeline plugin</li>
<li><a href="https://twitter.com/danveloper/status/611163598774235136">Location of ratpack.groovy is now configurable</a> via the Ratpack Gradle plugin says Dan Woods</li>
<li>An initial list of <a href="https://twitter.com/grailsframework/status/611107170692825088">Grails 3 plugins to be updated in priority</a></li>
<li><a href="https://twitter.com/sdkmanager/status/610783733642317824">Grails 3.0.2</a> available on GVM</li>
<li>Jorge Martín says <a href="https://twitter.com/arasthel92/status/610209132306694144">what&rsquo;s a hipster Android project looks like in Groovy</a></li>
<li>All of Ken Kousen&rsquo;s <a href="https://twitter.com/kenkousen/status/610077557065691136">Groovy videos for O&rsquo;Reilly are online</a></li>
<li>Graeme Rocher <a href="https://twitter.com/olavgg/status/608650670279946241">improved Grails 3 performance by 20%</a></li>
<li><a href="https://twitter.com/eugenekamenev/status/608124188868669441">OrientDB Groovy supports the Graph API</a>, Gremlin, and nice IntelliJ IDEA support says Eugene Kamenev</li>
<li>You can find <a href="https://twitter.com/jmiguel/status/606745439468187648">GORM in Copenhagen</a></li>
<li>Original <a href="https://twitter.com/jstrachan/status/606831442425999360">Groovy project founder is hacking Groovy again</a> within IntelliJ IDEA, in the shell and in Jenkins via the Job DSL for Continuous Integration and Continuous Delivery, and he&rsquo;s forgotten how much Groovy coding is fun!!</li>
<li>Craig Burke is investigating <a href="https://twitter.com/craigburke1/status/607973363105923072">using JFairy for sample value generation with GORM</a></li>
<li>Small <a href="https://twitter.com/ratpackweb/status/608063775825096706">Ratpack enhancement with sessions and Java 8</a> default interface methods</li>
<li><a href="https://twitter.com/Lspacewalker/status/606561199505801216">Danny Hyun is a happy Groovy contributor</a>!</li>
<li>Philipp Berner released a <a href="https://github.com/KeepSafe/dexcount-gradle-plugin">Gradle plugin that reports the number of method references in Android APKs</a></li>
<li><a href="https://twitter.com/aalmiray/status/606435403420299264">Lots of Gradle goodies to be found in the Griffon build</a>, says Andrés Almiray</li>
<li>Guillaume Laforge showing a <a href="https://twitter.com/Lspacewalker/status/606421415512948736">REST example with Ratpack using the byContent method</a></li>
<li><a href="https://twitter.com/RyanVanderwerf/status/606382108156657665">Groovy 2.4.3 is 2x faster than 2.2 at starting on the Lego EV3 robot</a> says Ryan Vanderwerf</li>
<li><a href="https://twitter.com/gr8conf/status/606126974940147712">Andrés Almiray is releasing Griffon 2.3 on stage with style</a> at GR8Conf Europe 2015!</li>
<li><a href="https://twitter.com/aalmiray/status/606022186135109632">Andrés Almiray pushed his first commit as a Groovy committer</a> at Apache</li>
<li><a href="https://twitter.com/sdkmanager/status/605875255974838273">JBake 2.4</a> available on GVM</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>The <a href="https://groups.google.com/forum/?hl=fr#!forum/paris-groovy-user-group">Paris Groovy user group is going to reboot</a> and come back in September, but starts with a fresh mailing-list on Google Groups. So if you&rsquo;re in the Paris area, join the band!</li>
<li><a href="https://twitter.com/ManningBooks/status/610553583940014080">Groovy in Action 2nd edition is available in print and eBook formats</a></li>
<li><a href="https://github.com/aaronzirbes/kinesis-http-adapter">Ratpack AWS Kinesis consumer</a> that forwards to an HTTP endpoint by Aaron Zirbes</li>
<li>Mario García releases <a href="http://mariogarcia.github.io/fnz/">FlambdaZ, a new Groovy functional library</a></li>
<li>Udacity offers a <a href="http://gradle.org/udacitys-gradle-for-android-training/">free online training on Gradle for Android</a> app development</li>
<li>A new <a href="https://twitter.com/infiniteskills/status/614547512419074048">practical Groovy programming video</a> by Ken Kousen on O&rsquo;Reilly</li>
<li><a href="https://twitter.com/theaviary/status/611622405581197312">New location for the Griffon mailing-lists</a></li>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="https://twitter.com/JacobAae/status/610569967864532993">Grails Diary</a> week 24</li>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="http://grydeske.net/news/show/98">Grails Diary</a> for weeks 22 and 23</li>
<li>The new <a href="https://twitter.com/glaforge/status/609462312840335362">Apache hosted mailing-lists are now archived on Nabble</a> like the old ones</li>
</ul>
<h2 id="podcasts">Podcasts</h2>
<ul>
<li><a href="https://twitter.com/groovypodcast/status/611944311551340545">Groovy podcast episode 14</a></li>
<li><a href="https://twitter.com/groovypodcast/status/616287886531522560">Groovy Podcast episode 15</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>The <a href="https://twitter.com/mittie/status/606845647459917825">source code of Groovy in Action (2nd ed) is available on Github</a></li>
<li>Dan Woods tweeted a <a href="https://twitter.com/danveloper/status/608298173208100864">hello world Ratpack in less than 140 characters</a></li>
<li>A sample <a href="https://gist.github.com/chiquitinxx/b661f27c647c3cfe5d38">REST API interaction from the client side with Groovy trait, JQuery and GrooScript</a></li>
<li>A <a href="https://twitter.com/timfox/status/608304503939584003">Vert.x app in Groovy in a tweet</a> by Tim Fox</li>
<li>Based on David Gageot&rsquo;s http library, Guillaume Laforge shows a little snippet for <a href="https://gist.github.com/glaforge/b5c55651f523b3281fc9">another hello world web server in a line of Groovy</a> code</li>
<li>Russ Hart has put online his <a href="https://github.com/rhart/hands-on-ratpack">Ratpack hands-on</a></li>
<li>A trick to <a href="https://dineshramitc.wordpress.com/2015/07/07/speed-up-gradle-builds-in-android-studio/">speedup Gradle builds in Android Studio</a></li>
<li>A <a href="http://forum.ratpack.io/Ratpack-with-React-hot-loader-support-td1141.html">Ratpack with React</a> hot loader support</li>
<li><a href="https://twitter.com/marc0der/status/612554222920404992">Marco Vermeulen loves Groovy&rsquo;s productivity</a> and shows a code snippet to demonstrate it</li>
<li>Eugene Kamenev published a <a href="https://twitter.com/eugenekamenev/status/613038251155152896">sample app mixing Groovy, Spring Boot, OrientDB</a> and Gremlin</li>
<li>Write your <a href="https://twitter.com/grooscript/status/609093126041550848">ReactiveX code in Groovy with GrooScript</a> and run in a Javascript environment</li>
<li>Arnaud Esteve shows a draft of a <a href="https://twitter.com/ArnaudEsteve/status/608926350028759041">Groovy DSL for Vert.x 3&rsquo;s router</a></li>
<li>Tim Fox&rsquo; tweetable <a href="https://twitter.com/timfox/status/608304503939584003">hello world Vert.x app in Groovy</a></li>
<li>Dan Woods&rsquo; tweetable <a href="https://twitter.com/danveloper/status/608298173208100864">Ratpack hello world app in Groovy</a></li>
<li>A <a href="https://twitter.com/grooscript/status/607613663978078208">GrooScript DSL to use the Google Maps API</a></li>
<li><a href="https://twitter.com/grooscript/status/607678436379885568">Rest API from the client-side with GrooScript</a></li>
</ul>
<h2 id="mailing-list-posts">Mailing-list posts</h2>
<ul>
<li>Discussing an <a href="http://www.groovy-lang.org/mailing-lists.html#nabble-td5725804">experimental @POJO AST transformation</a> by Paul King for a Groovy-less runtime</li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li><a href="https://twitter.com/gradle/status/615986939905351680">Netflix is hiring Gradle talents</a></li>
<li><a href="http://www.indeed.com/jobanalytics/jobtrends?q=gradle">Gradle job trend on the rise</a></li>
<li>A <a href="https://twitter.com/deigote/status/608695031046795265">Groovy and Grails job in Munich</a>, Germany</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>Dierk König announces that <a href="https://twitter.com/mittie/status/614872367106555904">Groovy in Action is back in stock at Amazon</a> since the beginning of the month</li>
<li><a href="https://twitter.com/mittie/status/610779780246577152">Groovy in Action 2nd edition was #1 on Amazon&rsquo;s hot new releases</a> says Dierk König</li>
<li><a href="http://www.groovy-lang.org/mailing-lists.html#nabble-td5725795">Groovy 2 tutorial cookbook</a> update by Duncan Dickinson available on LeanPub</li>
<li><a href="https://twitter.com/gradle/status/614527377440407552">Korean and Japanese books on Gradle</a> available</li>
<li>A new Manning MEAP upda<a href="http://manning.com/kapelonis/">te to the Java testing with Spock book</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy on Android for the Paris Android User Group</title><link>https://glaforge.dev/talks/2015/06/24/groovy-on-android-for-the-paris-android-user-group/</link><pubDate>Wed, 24 Jun 2015 00:00:00 +0200</pubDate><guid>https://glaforge.dev/talks/2015/06/24/groovy-on-android-for-the-paris-android-user-group/</guid><description>&lt;p>Yesterday, I had the pleasure to speak about Groovy on Android at the &lt;a href="http://www.meetup.com/fr/Android-Paris/events/223305279/">Paris Android User Group&lt;/a>. This is an evolution of my presentation from the DroidCon 2014 conference, with a few updates, in particular more coverage of SwissKnife.&lt;/p>
&lt;script async class="speakerdeck-embed" data-id="6284de7ee6d24035955a19bf6173a7a5" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js">&lt;/script></description><content:encoded>
<![CDATA[<p>Yesterday, I had the pleasure to speak about Groovy on Android at the <a href="http://www.meetup.com/fr/Android-Paris/events/223305279/">Paris Android User Group</a>. This is an evolution of my presentation from the DroidCon 2014 conference, with a few updates, in particular more coverage of SwissKnife.</p>
<script async class="speakerdeck-embed" data-id="6284de7ee6d24035955a19bf6173a7a5" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy in Action, 2nd edition is not a myth!</title><link>https://glaforge.dev/posts/2015/06/17/groovy-in-action-2nd-edition-is-not-a-myth/</link><pubDate>Wed, 17 Jun 2015 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2015/06/17/groovy-in-action-2nd-edition-is-not-a-myth/</guid><description>&lt;p>It took us a while to get it finished, but I assure you it was well worth the wait, and &lt;a href="http://www.manning.com/koenig2/">Groovy in Action, 2nd edition&lt;/a>, is real! I just received my copies this morning!&lt;/p>
&lt;p>Congrats to my fellow co-authors, and in particular to Dierk and Paul for their patience and dedication to this project, without whom the book would have never gone through its print stage!&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-c7438e8c451f831b08c5f145616d009f">
&lt;img src="https://glaforge.dev/img/misc/2015-06-17&amp;#43;09.22.37.jpg"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-c7438e8c451f831b08c5f145616d009f">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/2015-06-17&amp;#43;09.22.37.jpg"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p></description><content:encoded>
<![CDATA[<p>It took us a while to get it finished, but I assure you it was well worth the wait, and <a href="http://www.manning.com/koenig2/">Groovy in Action, 2nd edition</a>, is real! I just received my copies this morning!</p>
<p>Congrats to my fellow co-authors, and in particular to Dierk and Paul for their patience and dedication to this project, without whom the book would have never gone through its print stage!</p>
<p><figure>
  <a href="#img-c7438e8c451f831b08c5f145616d009f">
    <img src="/img/misc/2015-06-17&#43;09.22.37.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-c7438e8c451f831b08c5f145616d009f">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/2015-06-17&#43;09.22.37.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>My GR8Conf presentations slides</title><link>https://glaforge.dev/talks/2015/06/04/my-gr8conf-presentations-slides/</link><pubDate>Thu, 04 Jun 2015 00:00:00 +0200</pubDate><guid>https://glaforge.dev/talks/2015/06/04/my-gr8conf-presentations-slides/</guid><description>&lt;p>It&amp;rsquo;s been a busy and intense week here at Copenhagen for &lt;a href="http://gr8conf.eu/#/">GR8Conf Europe 2015&lt;/a>! Great interactions and discussions with the Groovy community, and flawless organization.&lt;/p>
&lt;p>I had the chance to present three talks this year, and I&amp;rsquo;m embedding my slides below. First of all, I&amp;rsquo;ve given the usual Groovy keynote, with a Groovy state of the union.&lt;/p>
&lt;script async class="speakerdeck-embed" data-id="a961bdc6faeb466199eded25f2a402a3" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js">&lt;/script>
&lt;p>Later on, I gave a talk on Groovy style! With interesting tips&amp;rsquo;n tricks, programming style advice.&lt;/p></description><content:encoded>
<![CDATA[<p>It&rsquo;s been a busy and intense week here at Copenhagen for <a href="http://gr8conf.eu/#/">GR8Conf Europe 2015</a>! Great interactions and discussions with the Groovy community, and flawless organization.</p>
<p>I had the chance to present three talks this year, and I&rsquo;m embedding my slides below. First of all, I&rsquo;ve given the usual Groovy keynote, with a Groovy state of the union.</p>
<script async class="speakerdeck-embed" data-id="a961bdc6faeb466199eded25f2a402a3" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>Later on, I gave a talk on Groovy style! With interesting tips&rsquo;n tricks, programming style advice.</p>
<script async class="speakerdeck-embed" data-id="5e39a3f0f8ae4cc58803bc70c903bd56" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>Lastly, with my Web API hat on, I talked about various tools for creating and testing Web APIs.</p>
<script async class="speakerdeck-embed" data-id="7ae90f9be4c54a15a2d6c723fbca623a" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #72</title><link>https://glaforge.dev/posts/2015/06/02/groovy-weekly-72/</link><pubDate>Tue, 02 Jun 2015 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2015/06/02/groovy-weekly-72/</guid><description>&lt;p>This 72th edition is being authored live from the Hackergarten session at &lt;a href="http://gr8conf.eu/#/">GR8Conf Europe 2015&lt;/a>, in Copenhagen (Denmark), where Groovy hackers are working on various contributions to the Groovy and Grails ecosystem, updating Grails 2 plugins to Grails 3, crafting a Groovy UI builder for Android, or work on some Gradle plugins. A busy evening!&lt;/p>
&lt;p>It’s been a little long while coming, but &lt;a href="https://twitter.com/mittie/status/604634440065761280">Groovy in Action 2nd edition has just been sent to the printers&lt;/a>! And the ebook formats will also be available this week!&lt;/p></description><content:encoded>
<![CDATA[<p>This 72th edition is being authored live from the Hackergarten session at <a href="http://gr8conf.eu/#/">GR8Conf Europe 2015</a>, in Copenhagen (Denmark), where Groovy hackers are working on various contributions to the Groovy and Grails ecosystem, updating Grails 2 plugins to Grails 3, crafting a Groovy UI builder for Android, or work on some Gradle plugins. A busy evening!</p>
<p>It’s been a little long while coming, but <a href="https://twitter.com/mittie/status/604634440065761280">Groovy in Action 2nd edition has just been sent to the printers</a>! And the ebook formats will also be available this week!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://ratpack.io/versions/0.9.17">Ratpack 0.9.17</a> released with new session support</li>
<li><a href="https://twitter.com/arasthel92/status/603990688481615875">SwissKnife 1.3.1</a> released with Android Res injection and better Parcelable support</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Immutable <a href="https://boxfuse.com/blog/gradle-plugin.html">infrastructure made easy with a Gradle</a> plugin</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li><a href="https://www.parleys.com/tutorial/groovy-light-java-8-1">Groovy, in the light of Java 8</a>, presented by Guillaume Laforge at Devoxx Belgium (registration required, but otherwise free content)</li>
<li><a href="http://greachconf.com/speakers/markus-schlichting-documentation-brought-to-life-asciidoctor-gradle/">Documentation brought to life with Asciidoctor and Gradle</a>, presented by Markus Schlichting at Greach</li>
<li>Marco Vermeulen on the <a href="http://greachconf.com/speakers/marco-vermeulen-groovy-environment-manager-2015/">Groovy enVironment Manager</a>, 2015 edition, at Greach</li>
<li><a href="http://greachconf.com/speakers/ivan-lopez-ast-groovy-transformers-more-than-meets-the-eye/">Groovy Transformers</a>: more than meets the eye by Ivan Lopez at Greach</li>
<li><a href="http://greachconf.com/speakers/jochen-theodorou-groovy-past-and-future/">Groovy past and future</a> by Jochen Theodorou at Greach</li>
<li>Claus Ibsen shares a video of <a href="https://twitter.com/davsclaus/status/603480717390172160">Groovy, Grails and Apache Camel</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li><a href="https://twitter.com/mittie/status/604634440065761280">Groovy in Action 2nd edition has been sent to the printers</a>! eBook versions available this week!</li>
<li>Object Computing Inc announces <a href="https://twitter.com/ObjectComputing/status/605496734265925632">Dave Klein and Collin Harrington are joining the Grails team</a></li>
<li>Schalk Cronjé learned about <a href="https://twitter.com/ysb33r/status/605693295164051456">Shoogr</a> at GR8Conf Europe and is thinking about integration with Groovy VFS</li>
<li>Dan Woods believes <a href="https://twitter.com/danveloper/status/605602603016310784">Ratpack and Spring Cloud is as natural a marriage as cloud native gets</a></li>
<li>Add <a href="https://twitter.com/ratpackweb/status/604446744198266881">LiveReload support to your Ratpack</a> app development</li>
<li>CardShifter&rsquo;s been a happy user of <a href="https://twitter.com/Cardshifter/status/604423648640925696">Groovy for its game design</a> work</li>
<li>Yann Le Moigne is looking for <a href="https://twitter.com/LeMoigneY/status/604310642737074177">feedback for two of his Ratpack modules</a></li>
<li><a href="https://twitter.com/sdkmanager/status/604002646530342912">GVM mentions some alternative names</a> getting traction</li>
<li>Jorge Martín says <a href="https://twitter.com/arasthel92/status/603843044950020096">you can&rsquo;t truly understand the power of Groovy AST transformations until you realize how limited Java APT is</a></li>
<li>Dierk König tells us that <a href="https://twitter.com/mittie/status/603286822396010496">Groovy in Action 2nd edition will be 912 pages</a> long</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li><a href="https://twitter.com/grooscript/status/605058483604570112">Create a REST API fake with Groovy</a>, using npm faker and JSON server</li>
<li>Eugene Kamenev shares the code of a <a href="https://twitter.com/eugenekamenev/status/604248101927391232">Spring Boot and OrientDB app in Groovy</a></li>
<li>Eugene Kamenev is working on <a href="https://github.com/eugene-kamenev/orientdb-groovy">Groovy wrappers around OrientDB</a>&rsquo;s APIs</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li><a href="https://twitter.com/mittie/status/605349387892604928">Groovy in Action 2nd edition comes with 10 thousand lines of code</a> tested automatically, notes Dierk König</li>
</ul>
<h2 id="podcasts">Podcasts</h2>
<ul>
<li><a href="https://twitter.com/groovypodcast/status/603575371255033856">Groovy Podcast episode 13</a> published</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #71</title><link>https://glaforge.dev/posts/2015/05/26/groovy-weekly-71/</link><pubDate>Tue, 26 May 2015 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2015/05/26/groovy-weekly-71/</guid><description>&lt;p>GR8Conf Europe 2015 is literally around the corner, as the conference will start next week in Copenhagen! I’m really excited to be joining the usual flock of Groovy afictionados and talk all things Groovy.&lt;/p>
&lt;p>Speaking of conferences, lots of new videos and accompanying slide decks have been released, so there are hours of binge watching ahead!&lt;/p>
&lt;p>By now, I think I can stop reminding you about the move of Groovy from Codehaus to the Apache Software Foundation? (and the move of the mailing-lists, issue tracker, sources, etc.)&lt;/p></description><content:encoded>
<![CDATA[<p>GR8Conf Europe 2015 is literally around the corner, as the conference will start next week in Copenhagen! I’m really excited to be joining the usual flock of Groovy afictionados and talk all things Groovy.</p>
<p>Speaking of conferences, lots of new videos and accompanying slide decks have been released, so there are hours of binge watching ahead!</p>
<p>By now, I think I can stop reminding you about the move of Groovy from Codehaus to the Apache Software Foundation? (and the move of the mailing-lists, issue tracker, sources, etc.)</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://twitter.com/grooscript/status/602547059418386435">GrooScript 1.1.1</a> released with associated Gradle plugin with support for Require.JS for improved Groovy / JavaScript interoperability</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Ken Kousen recounts his <a href="https://kousenit.wordpress.com/2015/05/25/upcoming-events-and-the-streak/">upcoming Groovy-related events and activities</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li><a href="https://github.com/kdabir/awesome-groovy">Awesome Groovy crosses 100 stars</a> and counting on Github</li>
<li><a href="https://twitter.com/gvmtool/status/603123176315424768">GVM is thinking about changing its name</a>, cast your vote!</li>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="http://grydeske.net/news/show/97">Grails Diary</a> week #21</li>
</ul>
<h2 id="presentations--special-greach-2015-coverage">Presentations — special Greach 2015 coverage</h2>
<ul>
<li><a href="http://greachconf.com/speakers/jorge-franco-grooscript-in-action/">Grooscript in action</a> by Jorge Franco</li>
<li>Andrés Almiray presented about the <a href="http://greachconf.com/speakers/andres-almiray-the-groovy-ecosystem/">Groovy ecosystem</a></li>
<li>Sport store chain <a href="http://greachconf.com/speakers/antonio-de-la-torre-alonso-torres-decathlon-sportmeeting-sports-a-new-grails-discipline/">Decathlon built its social network with Grails</a>, presented by Antonio de la Torre and Alonso Torres Ortiz</li>
<li><a href="http://greachconf.com/speakers/steve-pember-advanced-microservice-concerns/">Advanced microservice concerns</a> by Steve Pember</li>
<li><a href="http://greachconf.com/speakers/rene-groeschke-beyond-gradle-2-0/">Beyond Gradle 2.0</a> by René Gröschke</li>
<li><a href="http://greachconf.com/speakers/schalk-w-cronje-idiomatic-gradle-plugin-writing/">Idiomatic Gradle plugin writing</a> by Schalk Cronjé</li>
<li><a href="http://greachconf.com/speakers/rene-groeschke-building-android-apps-with-gradle/">Building Android apps with Gradle</a> by René Gröschke</li>
<li><a href="http://greachconf.com/speakers/markus-schlichting-documentation-brought-to-life-asciidoctor-gradle/">Documentation brought to life with Asciidoctor and Gradle</a> by Markus Schlichting</li>
<li>Marco Vermeulen gives a status update on the <a href="http://greachconf.com/speakers/marco-vermeulen-groovy-environment-manager-2015/">Groovy enVironment Manager in 2015</a></li>
<li><a href="http://greachconf.com/speakers/jeff-beck-securing-ratpack/">Securing Ratpack</a> by Jeff Beck</li>
<li>Göran Ehrsson on <a href="http://greachconf.com/speakers/goran-ehrsson-cut-your-grails-application-to-pieces-build-feature-plugins/">cutting your Grails app to pieces with feature plugins</a></li>
<li>Burt Beckwith shares <a href="http://greachconf.com/speakers/burt-beckwith-little-did-he-know/">interesting nuggets found during Grails development</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>With Codehaus&rsquo; EOL, <a href="https://twitter.com/gvmtool/status/601312032881844224">Groovy binary distributions prior to 2.2.2 are temporarily unavailable through GVM</a></li>
<li>Robin Bramley suggest an <a href="https://twitter.com/rbramley/status/601350754595639297">ideal Groovy snack for the break</a>!</li>
</ul>
<h2 id="snippets">Snippets</h2>
<ul>
<li>Erik Pragt shares a <a href="https://gist.github.com/bodiam/fe3f8448e8296bc2e07d">script to check the health status of a Grails 3 app</a></li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li><a href="https://twitter.com/mittie/status/603286822396010496">Groovy in Action will have a total of 912 pages</a>!</li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li>A &ldquo;fintech&rdquo; startup in London is looking for <a href="https://twitter.com/Charliedysonrec/status/601496181106171904">developers with Groovy, Grails and mobile experience</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>The <a href="https://twitter.com/CedricChampeau/status/603145483914215424">GR8Conf Android application has been updated</a> to display next week&rsquo;s European edition!</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #70</title><link>https://glaforge.dev/posts/2015/05/20/groovy-weekly-70/</link><pubDate>Wed, 20 May 2015 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2015/05/20/groovy-weekly-70/</guid><description>&lt;p>Last week, Codehaus has gone the way of the dodo. The lights are switching off. A page is turning.&lt;/p>
&lt;p>So please remember about our infrastructure changes, as we move the Groovy project through the incubation process at Apache: the &lt;a href="https://issues.apache.org/jira/browse/GROOVY/">JIRA issue tracker has moved&lt;/a>, and don’t forget that the &lt;a href="http://www.groovy-lang.org/mailing-lists.html">Groovy mailing-lists are also moving&lt;/a>. The &lt;a href="https://twitter.com/glaforge/status/588809178958925824">Apache Git repository&lt;/a> is now the new mainline, but you can contribute pull requests on the new &lt;a href="https://twitter.com/glaforge/status/588960808094326784">Apache Groovy Github mirror&lt;/a>. Be sure to update all your bookmarks, and register on the new mailing-list!&lt;/p></description><content:encoded>
<![CDATA[<p>Last week, Codehaus has gone the way of the dodo. The lights are switching off. A page is turning.</p>
<p>So please remember about our infrastructure changes, as we move the Groovy project through the incubation process at Apache: the <a href="https://issues.apache.org/jira/browse/GROOVY/">JIRA issue tracker has moved</a>, and don’t forget that the <a href="http://www.groovy-lang.org/mailing-lists.html">Groovy mailing-lists are also moving</a>. The <a href="https://twitter.com/glaforge/status/588809178958925824">Apache Git repository</a> is now the new mainline, but you can contribute pull requests on the new <a href="https://twitter.com/glaforge/status/588960808094326784">Apache Groovy Github mirror</a>. Be sure to update all your bookmarks, and register on the new mailing-list!</p>
<p>Beside that, there’s one particular item in the news below that I’d like to highlight here, that’s the <a href="https://speakerdeck.com/szpak/smarter-testing-java-code-with-spock-framework">great and thourough presentation on Spock</a>. If you want to learn the latest and greatest about Spock 1.0, that’s the slide deck you must have a look at!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://twitter.com/AndreyHihlovski/status/600703585027018753">Gretty 1.2.3</a> released</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Igor Shults shares the <a href="https://objectpartners.com/2015/05/14/list-of-groovy-versions-for-each-version-of-grails/">list of Groovy versions for each version of Grails</a></li>
<li>Jacob Severson talks about <a href="https://objectpartners.com/2015/05/19/dependable-microservices-via-lattice/">dependable microservices via Cloud Foundry&rsquo;s Lattice and Grails 3</a></li>
<li>Brendon Anderson shares his <a href="https://objectpartners.com/2015/05/12/my-first-ratpack-app-what-i-learned/">feedback on his first Ratpack application</a></li>
<li>The Groovy-Programming blog post features an article on <a href="http://groovy-programming.com/post/118804818009">Google Analytics measurement protocol in Groovy</a></li>
<li>David Dawson explains how to <a href="http://www.infoq.com/presentations/hexagonal-arch-grails">build a Grails application based on a hexagonal architecture</a></li>
<li>The Groovy Algorithms blog features a post on a <a href="http://groovyalgorithms.altervista.org/frequent-pattern-mining-model-implementation/">model implementation of frequent pattern mining</a></li>
<li>Algorithms in Groovy blog post on the <a href="http://groovyalgorithms.altervista.org/top-5-things-that-make-me-love-scripting-in-groovy/">top 5 that makes Groovy scripting loveable</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="http://grydeske.net/news/show/96">Grails Diary</a> week 20</li>
<li>The <a href="https://twitter.com/gvmtool/status/598961449193459712">Gradle GVM SDK vendor plugin</a> is now available in the Gradle plugin portal</li>
<li>The <a href="http://groovymn.tumblr.com/post/118772921517/last-month-in-gum">last month in GUM</a> news</li>
<li>The curated <a href="http://groovydevweekly.com/issues/5#start">Groovy Dev Weekly</a> news</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Marcin Zajączkowski gave an <a href="https://speakerdeck.com/szpak/smarter-testing-java-code-with-spock-framework">excellent and detailed presentation on smarter testing Java code with Spock</a>, with lots of Spock 1.0 coverage. A must see to master Spock!</li>
<li>From the GeeCon conference:
<ul>
<li>Iván López shares the <a href="https://twitter.com/ilopmar/status/599124700527087616">slides and code of his Grails presentation</a></li>
<li>Álvaro Sánchez-Mariscal shares the <a href="https://twitter.com/alvaro_sanchez/status/599178631827587072">slides and code of his Ratpack presentation and workshop</a></li>
</ul>
</li>
<li>From Devoxx France (but in French!):
<ul>
<li>“<a href="https://www.parleys.com/tutorial/plugin-gradle-prenez-le-controle-du-build">Plugin Gradle, prenez le contrôle du build</a>”, by Cédric Champeau and Eyal Lezmy</li>
<li>&ldquo;<a href="https://www.parleys.com/tutorial/groovy-et-son-process-de-release-nous-lavons-rendu-grooooooovy">Groovy et son process de release</a>&rdquo; by Guillaume Laforge, Cédric Champeau and Frédéric Simon</li>
</ul>
</li>
<li>From Greach, <a href="http://greachconf.com/speakers/alvaro-sanchez-mariscal-stateless-authentication-for-microservices/">stateless authentication for microservices with Spring Security in Grails</a>, by Álvaro Sánchez-Mariscal</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Ken Kousen published a <a href="https://twitter.com/kenkousen/status/600725847826366464">Groovy Fundamentals video course</a> for O&rsquo;Reilly Media</li>
<li>Craig Burke believes that <a href="https://twitter.com/craigburke1/status/598883871330062336">Grails 3 custom scripts can prove useful for generating Grails / Angular.JS CRUD apps</a></li>
<li>Craig Burke <a href="https://twitter.com/craigburke1/status/598912886031912962">upgraded his Angular.JS / Grails test app to Grails 3</a></li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>Dierk König says that <a href="https://twitter.com/mittie/status/600199545335771136">Groovy in Action 2nd ed offers the first full description of Groovy as a static language</a>, covering static compilation, static type checking, and type checking extensions</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>The Gradle summit early bird ends Friday</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #69</title><link>https://glaforge.dev/posts/2015/05/12/groovy-weekly-69/</link><pubDate>Tue, 12 May 2015 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2015/05/12/groovy-weekly-69/</guid><description>&lt;p>Groovy Weekly skipped a beat as I was very busy: I was very fortunate to spend my last week in California for various meetings, plus being a keynote speaker at JFrog’s &lt;a href="http://swampup.jfrog.com/">SwampUp&lt;/a> conference in Napa Valley. I had the chance to speak again about the Groovy release process and its history (based on my recent Devoxx &lt;a href="https://speakerdeck.com/glaforge/groovys-release-process-devoxx-2015">talk&lt;/a>).&lt;/p>
&lt;p>Congrats to the Gradle team for releasing &lt;a href="http://swampup.jfrog.com/">Gradle 2.4 with much faster builds&lt;/a>!&lt;/p>
&lt;p>Hurry up to &lt;a href="http://us4.campaign-archive2.com/?u=ac7af4c02d6cec67fe3198a63&amp;amp;id=6071c3f248">register to GR8Conf Europe 2015&lt;/a>, as registration closes on May 15th! Hurry up, and join the Groovy ecosystem fans gathering!&lt;/p></description><content:encoded>
<![CDATA[<p>Groovy Weekly skipped a beat as I was very busy: I was very fortunate to spend my last week in California for various meetings, plus being a keynote speaker at JFrog’s <a href="http://swampup.jfrog.com/">SwampUp</a> conference in Napa Valley. I had the chance to speak again about the Groovy release process and its history (based on my recent Devoxx <a href="https://speakerdeck.com/glaforge/groovys-release-process-devoxx-2015">talk</a>).</p>
<p>Congrats to the Gradle team for releasing <a href="http://swampup.jfrog.com/">Gradle 2.4 with much faster builds</a>!</p>
<p>Hurry up to <a href="http://us4.campaign-archive2.com/?u=ac7af4c02d6cec67fe3198a63&amp;id=6071c3f248">register to GR8Conf Europe 2015</a>, as registration closes on May 15th! Hurry up, and join the Groovy ecosystem fans gathering!</p>
<p>Reminder about our infrastructure changes, as we move the Groovy project through the incubation process at Apache: the <a href="https://issues.apache.org/jira/browse/GROOVY/">JIRA issue tracker has moved</a>, and don’t forget that the <a href="http://www.groovy-lang.org/mailing-lists.html">Groovy mailing-lists are also moving</a>. The <a href="https://twitter.com/glaforge/status/588809178958925824">Apache Git repository</a> is now the new mainline, but you can contribute pull requests on the new <a href="https://twitter.com/glaforge/status/588960808094326784">Apache Groovy Github mirror</a>. Be sure to update all your bookmarks, and register on the new mailing-list!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://discuss.gradle.org/t/gradle-2-4-released/9471">Gradle 2.4</a> released</li>
<li><a href="http://ratpack.io/versions/0.9.16/">Ratpack 0.9.16</a> released</li>
<li><a href="http://www.craigburke.com/document-builder/">Groovy Document Builder v0.4</a> by Craig Burke</li>
<li><a href="http://grengine.ch/manual.html#grengine-and-grape">Grengine 1.0.3</a> released</li>
<li><a href="http://www.jexler.net">Jexler 1.0.16</a> released</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Mirosław Gołda on <a href="http://allegrotech.io/automated-tests-with-geb-spock-and-groovy.html">automated tests with Geb, Spock and Groovy</a></li>
<li>MrHaki&rsquo;s Groovy Goodness: <a href="http://mrhaki.blogspot.dk/2015/05/groovy-goodness-share-data-in.html">Share data in concurrent environment with dataflow variables</a></li>
<li><a href="http://blog.agileorbit.com/2015/05/11/Grails3-OAuth2.html">APIs with Grails 3 and OAuth 2</a> by Bobby Warner</li>
<li><a href="https://kousenit.wordpress.com/2015/05/08/concurrent-kitties-using-gpars/">Concurrent kitties using GPars</a> by Ken Kousen (spoiler, no animals harmed!)</li>
<li><a href="http://grooscript.org/gradle/templating.html">GrooScript Groovy templating on the browser client-side</a></li>
<li>Eric MacAdie writes about the <a href="http://www.macadie.net/2015/05/09/hidden-annotation-in-groovy-validators/">@Hidden annotation in Groovy Validators</a></li>
<li><a href="https://discuss.gradle.org/t/testing-plugin-compatibility-against-gradle-versions/9473">Testing plugin compatibility against Gradle versions</a> by Schalk Cronjé on the Gradle forums</li>
<li><a href="http://www.dahlgren.so/docker/2015/04/12/Deploying-Grails-Apps-To-Docker/">Deploying Grails apps to Docker</a> by Ron Dahlgren</li>
<li><a href="http://groovyalgorithms.altervista.org/groovy-profiling-with-gprof/">Groovy profiling with Gprof</a></li>
<li><a href="http://groovyalgorithms.altervista.org/groovy-benchmarking-with-gbench/">Groovy benchmarking with GBench</a></li>
<li><a href="https://objectpartners.com/2015/04/29/using-gradle-and-bower-to-manage-jscss-dependencies/">Using Gradle and Bower to manage JavaScript and CSS dependencies</a> by Patrick Double</li>
<li><a href="http://www.intelligrape.com/blog/compile-groovyscript-at-runtime-allow-caching-of-compiled-source-to-avoid-recompilation-at-runtime-using-groovyclassloader/">Compile Groovy scripts at runtime and allow caching</a> of compiled source to avoid recompilation using GroovyClassLoader by Tarun Pareek</li>
<li>StackOverflow Groovy superstar Tim Yates answers a question on <a href="http://stackoverflow.com/questions/29976665/why-groovys-map-scales-better-than-array">map vs array performance</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li><a href="http://grydeske.net/news/show/95">Grails Diary</a> week 19 by Jakob Aae Mikkelsen</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Cédric Champeau presented at the Bordeaux JUG on Gradle&rsquo;s way of convention over configuration</li>
<li>More SpringOne2GX 2014 videos released:
<ul>
<li><a href="http://www.infoq.com/presentations/grails-plugin-testing">Grails plugin testing strategies</a> by Baruch Sadogursky</li>
<li>Owen Rubel talks about <a href="http://www.infoq.com/presentations/api-grails-dry">API abstraction and API chaining in Grails</a></li>
<li><a href="http://www.infoq.com/presentations/groovy-rest-nosql">Groovy vampires: combining Groovy, REST, NoSQL</a>, and more by Ken Kousen</li>
<li>Fabrice Matrat dives into <a href="http://www.infoq.com/presentations/single-page-app-ravejs-grails">single page applications with Grails and RaveJS</a></li>
<li><a href="http://www.infoq.com/presentations/web-mobile-apps-spring-groovy">Conquering content-enabled web and mobile applications with Spring and Groovy</a> by Russ Danner</li>
<li><a href="http://www.infoq.com/presentations/ast-transformations">Writing AST transformations, get practical in 90 minutes</a> by Baruch Sadogursky and Fred Simon</li>
<li>The <a href="http://www.infoq.com/presentations/future-gradle-javascript">future of Gradle</a>, the ultimate build system, by Hans Dockter</li>
</ul>
</li>
<li>Greach videos too:
<ul>
<li>Trisha Gee on <a href="http://java.dzone.com/articles/groovy-vs-java-testing-video">Groovy vs Java for testing</a></li>
<li><a href="http://greachconf.com/speakers/ruben-mondejar-andreu-introducing-workflow-architectures-using-grails/">Introducing workflow architectures using Grails</a> by Rubén Mondéjar Andreu</li>
<li><a href="http://greachconf.com/speakers/jeff-beck-grails-and-cassandra/">Grails and Cassandra</a> by Jeff Beck</li>
<li><a href="http://greachconf.com/speakers/alexander-sascha-klein-groovy-on-the-shell/">Groovy on the shell</a> by Alexander Klein</li>
<li><a href="http://greachconf.com/speakers/fatima-casau-use-groovy-grails-in-your-spring-boot-projects-don-t-be-afraid/">Use Groovy and Grails in your Spring Boot projects</a>, by Fatima Casau</li>
</ul>
</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Cédric Champeau echoes that the <a href="https://twitter.com/CedricChampeau/status/593347714244575232">Groovy project is looking for developers to help on the Groovy Eclipse project</a></li>
<li>Dierk König says that Groovy in Action chapter 8 is the first <a href="https://twitter.com/mittie/status/597649018676707328">complete exposition of all Groovy runtime metaprogramming capabilities</a></li>
<li>The <a href="https://twitter.com/mittie/status/596753493270110208">code base of Groovy in Action 2nd ed contains over 1000 power assert statements</a> counted Dierk König</li>
<li><a href="https://twitter.com/theaviary/status/597053097089826817">Griffon 2.3 will feature JavaFX specific AST transformations</a></li>
<li>Ratpack 0.9.17 will let you use <a href="https://twitter.com/ratpackweb/status/594753885266378752">async promises from synchronous APIs</a></li>
<li>Robert Zakrzewski is a <a href="https://twitter.com/ratpackweb/status/595515092885643264">new contributor to the Ratpack</a> project</li>
<li><a href="https://twitter.com/gvmtool/status/595553687617036289">Gradle 2.4</a> available on GVM</li>
<li><a href="https://twitter.com/aheritier/status/595671992797388800">SonarQube&rsquo;s Groovy plugin now supports code coverage</a> notes Arnaud Héritier</li>
</ul>
<h2 id="podcasts">Podcasts</h2>
<ul>
<li><a href="https://www.youtube.com/watch?v=E36Dw5xbhzQ">Groovy podcast</a> episode 12 is out</li>
</ul>
<h2 id="screencasts">Screencasts</h2>
<ul>
<li>Bertrand Goetzmann published a screencast about <a href="https://www.youtube.com/watch?v=LI6EIfmt26I">creating micro services with Grails 3</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Cédric Champeau shows how he integrated <a href="https://github.com/melix/bdxjug2015/blob/master/gradle/pdf.gradle">deck2pdf into his Gradle build</a></li>
<li>Code samples for <a href="http://asoftwareguy.com/2015/03/30/spring-security-with-grails-3/">Grails 3 and Spring Security</a></li>
<li>Sergio del Amo authored a <a href="https://github.com/sdelamo/wp-api-groovy">Groovy script to interact with Wordpress&rsquo; JSON REST API</a></li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li>New Grails&rsquo; home <a href="https://twitter.com/grailsframework/status/596081391013466113">Object Computing is hiring more Grails experts</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="http://us4.campaign-archive2.com/?u=ac7af4c02d6cec67fe3198a63&amp;id=6071c3f248">GR8Conf Europe 2015 registration is closing May 15th</a>, be fast!</li>
<li>You can see all the <a href="https://twitter.com/greachconf/status/595855035117895680">speakers interviews from Greach</a> on YouTube</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #68</title><link>https://glaforge.dev/posts/2015/04/28/groovy-weekly-68/</link><pubDate>Tue, 28 Apr 2015 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2015/04/28/groovy-weekly-68/</guid><description>&lt;p>Remember the Android support of Groovy 2.4? Well, the new &lt;a href="https://twitter.com/mohitpandey/status/592723947311001600">New York Times Android application, written in Groovy&lt;/a> and using RxJava is now live! If a hugely popular Android app is leveraging Groovy, yours can too!&lt;/p>
&lt;p>With Groovy’s move to the Apache Software Foundation, &lt;a href="https://twitter.com/sbglasius/status/591322009214160896">don’t forget to star the new Groovy Github repository&lt;/a>!&lt;/p>
&lt;p>Don’t miss the interesting interviews of &lt;a href="http://www.infoq.com/news/2015/04/grails-3">Jeff Brown&lt;/a> and &lt;a href="https://www.voxxed.com/blog/2015/04/pushing-groovy-to-the-speed-of-java-and-beyond/">Guillaume Laforge&lt;/a>.&lt;/p>
&lt;p>Reminder about our infrastructure changes, as we move the Groovy project through the incubation process at Apache: the &lt;a href="https://issues.apache.org/jira/browse/GROOVY/">JIRA issue tracker has moved&lt;/a>, and don’t forget that the &lt;a href="http://www.groovy-lang.org/mailing-lists.html">Groovy mailing-lists are also moving&lt;/a>. The novelty this week is that the &lt;a href="https://twitter.com/glaforge/status/588809178958925824">Apache Git repository&lt;/a> is now the new mainline, but you can contribute pull requests on the &lt;a href="https://twitter.com/glaforge/status/588960808094326784">Apache Groovy Github mirror&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>Remember the Android support of Groovy 2.4? Well, the new <a href="https://twitter.com/mohitpandey/status/592723947311001600">New York Times Android application, written in Groovy</a> and using RxJava is now live! If a hugely popular Android app is leveraging Groovy, yours can too!</p>
<p>With Groovy’s move to the Apache Software Foundation, <a href="https://twitter.com/sbglasius/status/591322009214160896">don’t forget to star the new Groovy Github repository</a>!</p>
<p>Don’t miss the interesting interviews of <a href="http://www.infoq.com/news/2015/04/grails-3">Jeff Brown</a> and <a href="https://www.voxxed.com/blog/2015/04/pushing-groovy-to-the-speed-of-java-and-beyond/">Guillaume Laforge</a>.</p>
<p>Reminder about our infrastructure changes, as we move the Groovy project through the incubation process at Apache: the <a href="https://issues.apache.org/jira/browse/GROOVY/">JIRA issue tracker has moved</a>, and don’t forget that the <a href="http://www.groovy-lang.org/mailing-lists.html">Groovy mailing-lists are also moving</a>. The novelty this week is that the <a href="https://twitter.com/glaforge/status/588809178958925824">Apache Git repository</a> is now the new mainline, but you can contribute pull requests on the <a href="https://twitter.com/glaforge/status/588960808094326784">Apache Groovy Github mirror</a>.</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://discuss.gradle.org/t/gradle-2-4-rc-1-is-now-available-for-testing/9282">Gradle 2.4-rc-1</a> released with annotation processing with Groovy code</li>
<li>Online presentation service Prezi releases a <a href="https://twitter.com/dvigovszky/status/590889954655473664">Haskell plugin for Gradle</a></li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>MrHaki’s on the writing spree:
<ul>
<li>Grails Goodness
<ul>
<li><a href="http://mrhaki.blogspot.fr/2015/04/grails-goodness-adding-health-check.html">adding health check indicators</a></li>
<li><a href="http://mrhaki.blogspot.fr/2015/04/grails-goodness-log-startup-info.html">log startup info</a></li>
<li><a href="http://mrhaki.blogspot.fr/2015/04/grails-goodness-save-application-pid-in.html">save application PID in file</a></li>
</ul>
</li>
<li>Gradle Goodness
<ul>
<li><a href="http://mrhaki.blogspot.fr/2015/04/gradle-goodness-handle-copying.html">handle copying duplicate files</a></li>
<li><a href="http://mrhaki.blogspot.fr/2015/04/gradle-goodness-use-git-commit-id-in.html">usee Git commit id in build script</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="http://www.grails.info/2015/04/23/new-blog-groovy-now-runs-faster-than-java-on-mobile/">Groovy now runs faster on mobile</a> than Java says Grails Info news site</li>
<li>Iván López on <a href="http://www.kaleidos.net/blog/1071/talking-about-groovy-in-vienna/">talking about Groovy</a> in Vienna</li>
</ul>
<h2 id="interviews">Interviews</h2>
<ul>
<li>Guillaume Laforge interviewed by Voxxed during Devoxx France, talking about <a href="https://www.voxxed.com/blog/2015/04/pushing-groovy-to-the-speed-of-java-and-beyond/">Groovy&rsquo;s Android support, Grails 3.0, Groovy in RESTful APIs</a> and more</li>
<li>Matt Raible interview <a href="http://www.infoq.com/news/2015/04/grails-3">Jeff Brown on Grails 3</a>, built on Spring Boot and Gradle</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>The <a href="https://twitter.com/mohitpandey/status/592723947311001600">New York Times Android application written in Groovy</a> and using RxJava is now live!</li>
<li><a href="http://www.itexto.net/devkico/?p=2156">Grails Brasil launches its new website</a>, promising to open source its code base so other user groups can benefit from it</li>
<li><a href="https://twitter.com/gvmtool/status/592940974470537216">Gradle 2.4-rc-1</a> available on GVM</li>
<li>Gradle 2.4-rc-1 release notes covers <a href="https://gradle.org/docs/2.4-rc-1/release-notes#support-for-%E2%80%9Cannotation-processing%E2%80%9D-of-groovy-code">annotation processing of Groovy code</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li><a href="http://fr.slideshare.net/nareshak/pragmatic-browser-automation-with-geb-gids-2015">Programmatic browser automation with Geb</a> by Naresha K at the Great Indian Developer Summit</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Wtih Groovy moving to Apache, so did its Github repository. <a href="https://twitter.com/sbglasius/status/591322009214160896">Don&rsquo;t forget to star the new Groovy repo on Github</a>!</li>
<li>Dagger developer Jake Wharton mentions <a href="https://twitter.com/JakeWharton/status/591242052202934273">support for annotation in Groovy code via generated stubs with Gradle 2.4</a></li>
<li><a href="https://twitter.com/grooscript/status/591713872568033282">GrooScript moved its Groovy Ecosystem demo</a> to a new location</li>
<li>Voxxed mentions <a href="https://twitter.com/voxxed/status/591231196245417984">Groovy moving to the speed of Java</a></li>
<li>Dierk König mentions that the script of <a href="https://twitter.com/mittie/status/592937821402443776">Groovy in Action, 2nd edition, has over 1600 commits</a> on its private Git repository</li>
<li><a href="https://twitter.com/gvmtool/status/591149663782359040">Lazybones 0.8.1</a> available on GVM</li>
<li>Dan Woods highlights Ratpack 0.9.15&rsquo;s Groovy extensions <a href="https://twitter.com/danveloper/status/590950625254354944">easily transform RxJava Observables into Ratpack Promises</a></li>
</ul>
<h2 id="podcasts">Podcasts</h2>
<ul>
<li><a href="https://twitter.com/groovypodcast/status/590970033410547713">Groovy podcast episode 11</a> available</li>
</ul>
<h2 id="screencasts">Screencasts</h2>
<ul>
<li>The NetBeans team recorded a screencast about Minecraft Forge showcasing its <a href="https://twitter.com/netbeans/status/592059252702851072">Gradle support</a></li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li><a href="https://twitter.com/mittie/status/591272887786569730">Groovy in Action, 2nd edition, now in proof-reading stage</a>!</li>
<li>New book, in Portuguese, “<a href="http://www.casadocodigo.com.br/products/livro-grails">Falando de Grails</a>”</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li><a href="https://gist.github.com/jexp/7c6997242c001abfb2cd">Load JSON data into Neo4J with Groovy</a> and Cypher</li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li>A <a href="https://twitter.com/sbglasius/status/590575191119507459">job for beer lovers and Grails experts</a></li>
</ul>
<h2 id="google-posts">Google+ posts</h2>
<ul>
<li>New improvements to Tim Roes’ <a href="https://plus.google.com/u/0/+TimRoes/posts/f5LrmstNqTE?cfem=1">Groovy Playground</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>GR8Conf Europe will feature <a href="https://twitter.com/gr8conf/status/592899671519027201">more Groovy puzzlers</a></li>
<li><a href="https://twitter.com/gr8conf/status/592898886848671747">Grails event driven plugins</a> at GR8Conf Europe by Göran Ehrsson</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #67</title><link>https://glaforge.dev/posts/2015/04/21/groovy-weekly-67/</link><pubDate>Tue, 21 Apr 2015 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2015/04/21/groovy-weekly-67/</guid><description>&lt;p>No big focus this week in the editorial, but as in the previous edition, I’d like to remind you about the following infrastructure changes, as we move the Groovy project through the incubation process at Apache: the &lt;a href="https://issues.apache.org/jira/browse/GROOVY/">JIRA issue tracker has moved&lt;/a>, and don’t forget that the &lt;a href="http://www.groovy-lang.org/mailing-lists.html">Groovy mailing-lists are also moving&lt;/a>. The novelty this week is that the &lt;a href="https://twitter.com/glaforge/status/588809178958925824">Apache Git repository&lt;/a> is now the new mainline, but you can contribute pull requests on the &lt;a href="https://twitter.com/glaforge/status/588960808094326784">Apache Groovy Github mirror&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>No big focus this week in the editorial, but as in the previous edition, I’d like to remind you about the following infrastructure changes, as we move the Groovy project through the incubation process at Apache: the <a href="https://issues.apache.org/jira/browse/GROOVY/">JIRA issue tracker has moved</a>, and don’t forget that the <a href="http://www.groovy-lang.org/mailing-lists.html">Groovy mailing-lists are also moving</a>. The novelty this week is that the <a href="https://twitter.com/glaforge/status/588809178958925824">Apache Git repository</a> is now the new mainline, but you can contribute pull requests on the <a href="https://twitter.com/glaforge/status/588960808094326784">Apache Groovy Github mirror</a>.</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://twitter.com/grooscript/status/589123058318860290">GrooScript 1.0.2</a> released</li>
<li><a href="https://github.com/akhikhl/gretty/blob/master/RELEASE.md#gretty-122-release-announcement">Gretty 1.2.2</a> released</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Paul Krill of InfoWorld on <a href="http://www.cio.com/article/2909687/developer/grails-web-framework-finds-home-at-object-computing.html">Grails finding a new home at OCI</a></li>
<li>An overview of <a href="http://java.dzone.com/articles/whats-new-grails-3">what&rsquo;s new in Grails 3</a> by Michael Scharhag</li>
<li>Geb Gems: <a href="http://blog.jdriven.com/2015/04/geb-gems-running-geb-spock-tests-maven/">Running Geb Spock tests with Maven</a> by Albert van Veen</li>
<li>Jeff Beck on <a href="http://beckje01.com/blog/2015/04/18/springsecurity-impersonate-users-custom-roles/">impersonating user custom roles with Spring Security in Grails</a></li>
<li>MrHaki&rsquo;s Gradle Goodness: <a href="http://mrhaki.blogspot.dk/2015/04/gradle-goodness-alter-start-scripts.html">alter start scripts from application plugin</a></li>
<li>MrHaki&rsquo;s Grails Goodness: <a href="http://mrhaki.blogspot.fr/2015/04/grails-goodness-set-log-level-for.html">set log level for Grails artifacts</a></li>
<li>MrHaki&rsquo;s Grails Goodness: <a href="http://mrhaki.blogspot.fr/2015/04/grails-goodness-add-some-color-to-our.html">add some color to our logging</a></li>
<li><a href="http://www.intelligrape.com/blog/making-a-domain-non-persistent/">Making a Grails domain class non-persistent</a> by Vinay Prajapati</li>
<li><a href="http://www.intelligrape.com/blog/parsing-url-mappings-in-grails/">Parsing URL mappings in Grails</a> by Sandeep Poonia</li>
<li><a href="http://www.intelligrape.com/blog/spring-events-with-grails/">Spring events with Grails</a></li>
<li>Ken Kousen writes <a href="https://kousenit.wordpress.com/2015/04/16/groovy-posts-in-other-places/">Groovy related blog posts</a> elsewhere too</li>
<li><a href="https://weblogs.java.net/blog/manningpubs/archive/2013/03/18/building-java-web-application-gradle">Building a Java web application with Gradle</a> by Benjamin Muschko</li>
<li>Short on time? Switch to <a href="http://www.javacodegeeks.com/2015/04/short-on-time-switch-to-groovy-for-unit-testing.html">Groovy and Spock for unit testing</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li><a href="https://gradle.org/welcoming-lari-hotari-and-cedric-champeau-to-gradle-team/">Gradleware is officially welcoming Cédric Champeau and Lari Hotari</a> into the Gradle team</li>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="http://grydeske.net/news/show/92">Grails Diary</a> week 16</li>
<li>You can still <a href="https://youtrack.jetbrains.com/issue/IDEA-136970">vote for Grails 3 support in IntelliJ IDEA</a></li>
<li>A <a href="http://groovydevweekly.com/issues/1?#start">new curated list of Groovy news</a> by Sergio del Amo</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li><a href="http://www.infoq.com/presentations/groovy-grails-puzzler">Groovy and Grails puzzlers</a>, traps, pitfalls, and end cases by Baruch Sadogursky and Frédéric Simon, recorded at SpringOne2GX 2015</li>
<li>Guillaume Laforge shares the links to his <a href="https://glaforge.dev/talks/2015/04/17/devoxx-2015-presentations/">Devoxx France presentations on Groovy</a></li>
<li><a href="https://www.youtube.com/watch?v=3pdFdbvaUyg">Confessions of a Java developer who fell in love with Groovy</a> (in Hebrew)</li>
<li>Iván López on <a href="https://twitter.com/ilopmar/status/588338534366457856">metaprogramming with Groovy</a> at the Confess conference with slides and sample code</li>
<li>Alexander Klein on <a href="http://fr.slideshare.net/sascha_klein/groovy-on-the-shell">Groovy in the Shell</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>The <a href="https://twitter.com/GebFramework/status/588457692651356160">Geb framework developers are happy with their move to Github issues</a></li>
<li>Guillaume Laforge shares the new <a href="https://twitter.com/glaforge/status/588673146355613696">Apache Groovy mailing-list archives</a> links</li>
<li>Guillaume Laforge gives the link to the <a href="https://twitter.com/glaforge/status/588809178958925824">Apache Git web interface</a></li>
<li>You can <a href="https://twitter.com/glaforge/status/588960808094326784">send pull requests to Apache&rsquo;s Groovy git mirror on Github</a> notes Guillaume Laforge</li>
<li>Søren Berg Glasius reminds developers they can now <a href="https://twitter.com/sbglasius/status/589088682512027648">star the Groovy git mirror on Github</a> for good reputation</li>
<li><a href="https://twitter.com/CedricChampeau/status/588968404633157632">Cédric Champeau received his JavaOne Rock Star award</a>!</li>
<li>Buildship, a <a href="https://twitter.com/Gradleware/status/589102675381379072">Gradle plugin for Eclipse</a> by GradleWare and Vogella</li>
<li>Craig Burke is having fun with <a href="https://twitter.com/craigburke1/status/589107422754693120">laying out tables in his Groovy Document Builder for v0.4</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="https://twitter.com/gr8conf/status/588319368477458432">Object Computing becomes GR8Conf Europe gold sponsor</a></li>
<li>The <a href="https://twitter.com/JacobAae/status/589112241426931713">GR8Conf Europe beers are ready</a>!</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Devoxx 2015 Presentations</title><link>https://glaforge.dev/talks/2015/04/17/devoxx-2015-presentations/</link><pubDate>Fri, 17 Apr 2015 00:00:00 +0200</pubDate><guid>https://glaforge.dev/talks/2015/04/17/devoxx-2015-presentations/</guid><description>&lt;p>I had the pleasure of presenting 3 talks at Devoxx France last week (plus the Cast Codeurs podcast live recording), and I wanted to share with you the slides of those presentations.&lt;/p>
&lt;h2 id="the-groovy-release-process">The Groovy release process&lt;/h2>
&lt;p>With Cédric Champeau and Frédéric Simon&lt;/p>
&lt;script async class="speakerdeck-embed" data-id="e3398ca68b9d4ef2a65309f92bb10d16" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js">&lt;/script>
&lt;h2 id="groovy-with-style">Groovy with style&lt;/h2>
&lt;script async class="speakerdeck-embed" data-id="d46bba738e3a469281caecb3a68803b9" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js">&lt;/script>
&lt;h2 id="machine-learning-introduction">Machine Learning introduction&lt;/h2>
&lt;p>In French, with Didier Girard&lt;/p>
&lt;script async class="speakerdeck-embed" data-id="20d59cbfef164757baf7599d5e88c141" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js">&lt;/script></description><content:encoded>
<![CDATA[<p>I had the pleasure of presenting 3 talks at Devoxx France last week (plus the Cast Codeurs podcast live recording), and I wanted to share with you the slides of those presentations.</p>
<h2 id="the-groovy-release-process">The Groovy release process</h2>
<p>With Cédric Champeau and Frédéric Simon</p>
<script async class="speakerdeck-embed" data-id="e3398ca68b9d4ef2a65309f92bb10d16" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<h2 id="groovy-with-style">Groovy with style</h2>
<script async class="speakerdeck-embed" data-id="d46bba738e3a469281caecb3a68803b9" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<h2 id="machine-learning-introduction">Machine Learning introduction</h2>
<p>In French, with Didier Girard</p>
<script async class="speakerdeck-embed" data-id="20d59cbfef164757baf7599d5e88c141" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #66</title><link>https://glaforge.dev/posts/2015/04/14/groovy-weekly-66/</link><pubDate>Tue, 14 Apr 2015 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2015/04/14/groovy-weekly-66/</guid><description>&lt;p>We’re only mid April but what a year it’s been so far! From &lt;a href="https://glaforge.dev/posts/2015/01/19/the-groovy-project-is-looking-for-a-new-home/">Groovy &amp;amp; Grails’ loss of funding&lt;/a>, leading &lt;a href="http://interact.stltoday.com/pr/business/PR040915114421060">Grails to a new home at Object Computing Inc.&lt;/a> and &lt;a href="https://glaforge.dev/posts/2015/03/04/groovy-projects-intends-to-join-the-apache-software-foundation/">Groovy at the Apache Software Foundation&lt;/a>, to &lt;a href="http://restlet.com/blog/2015/03/02/head-of-groovy-project-joins-restlet-to-lead-api-development-tools/">Guillaume Laforge joining Restlet&lt;/a> and &lt;a href="https://twitter.com/cedricchampeau/">Cédric joining GradleWare&lt;/a>. Against all odds, &lt;a href="https://glaforge.dev/posts/2015/01/21/groovy-2-4-released/">Groovy released its 2.4 version with the Android support&lt;/a>, and the big &lt;a href="http://grails.github.io/grails-doc/3.0.x/guide/introduction.html#whatsNew">3.0 release of Grails&lt;/a> saw the light of day. And on another personal note… I’ve &lt;a href="https://instagram.com/p/1YIA2KANZD/">finished my first marathon&lt;/a>! What’s next for the rest of the year?&lt;/p></description><content:encoded>
<![CDATA[<p>We’re only mid April but what a year it’s been so far! From <a href="https://glaforge.dev/posts/2015/01/19/the-groovy-project-is-looking-for-a-new-home/">Groovy &amp; Grails’ loss of funding</a>, leading <a href="http://interact.stltoday.com/pr/business/PR040915114421060">Grails to a new home at Object Computing Inc.</a> and <a href="https://glaforge.dev/posts/2015/03/04/groovy-projects-intends-to-join-the-apache-software-foundation/">Groovy at the Apache Software Foundation</a>, to <a href="http://restlet.com/blog/2015/03/02/head-of-groovy-project-joins-restlet-to-lead-api-development-tools/">Guillaume Laforge joining Restlet</a> and <a href="https://twitter.com/cedricchampeau/">Cédric joining GradleWare</a>. Against all odds, <a href="https://glaforge.dev/posts/2015/01/21/groovy-2-4-released/">Groovy released its 2.4 version with the Android support</a>, and the big <a href="http://grails.github.io/grails-doc/3.0.x/guide/introduction.html#whatsNew">3.0 release of Grails</a> saw the light of day. And on another personal note… I’ve <a href="https://instagram.com/p/1YIA2KANZD/">finished my first marathon</a>! What’s next for the rest of the year?</p>
<p>With the Greach conference just over, there’s a lot of great and groovy content available in the presentations section! Take your time to have a look at this impressive lineup and interesting material.</p>
<p>Speaking of conferences, rush to buy early bird tickets for GR8Conf Europe! You only have till tomorrow!</p>
<p>As in the previous edition, I’d like to remind you about the following infrastructure changes, as we move the Groovy project through the incubation process at Apache: the <a href="https://issues.apache.org/jira/browse/GROOVY/">JIRA issue tracker has moved</a>, and don’t forget that the <a href="http://www.groovy-lang.org/mailing-lists.html">Groovy mailing-lists are also moving</a>.</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://twitter.com/MGrzejszczak/status/583875742053879808">Spock Subjects Collaborators Extension 1.1.0</a> released by Marcin Grzejszczak</li>
<li><a href="https://twitter.com/craigburke1/status/585611941072211969">Document Builder 0.3.1</a> released by Craig Burke with spacing and positioning fixes</li>
<li><a href="https://twitter.com/AndreyHihlovski/status/586809300678483969">Gretty 1.2.1</a> released with WebSocket support</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Ken Kousen writes about <a href="https://www.accelebrate.com/blog/kicking-ast-taking-names/">kicking AST and taking names</a></li>
<li>MrHaki&rsquo;s <a href="http://mrhaki.blogspot.fr/2015/04/greach-2015-conference-report.html">Greach conference report</a></li>
<li>Jochen Theodorou on <a href="http://blackdragsview.blogspot.de/2015/04/about-being-paid-oss-developer-for.html">being a paid OSS developer for Groovy</a></li>
<li><a href="http://interact.stltoday.com/pr/business/PR040915114421060">Grails has a new home at OCI</a></li>
</ul>
<h2 id="interviews">Interviews</h2>
<ul>
<li>Cédric Champeau interviewed by Voxxed during the Devoxx France conference: &ldquo;<a href="https://www.voxxed.com/blog/2015/04/cedric-champeau-groovy-is-everybodys-language-to-evolve/">Groovy is everybody&rsquo;s language to evolve</a>&rdquo;</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>The <a href="https://twitter.com/groovypodcast/status/585446151601704960">Groovy podcast now has a pretty nice logo</a>!</li>
<li><a href="https://twitter.com/GebFramework/status/587525108882866177">Geb&rsquo;s issue tracker moved to Github</a></li>
</ul>
<h2 id="mailing-list-posts">Mailing-list posts</h2>
<ul>
<li>If you or <a href="https://twitter.com/CedricChampeau/status/587353266457018368">your company is interested in helping or taking over the development of the Groovy Eclipse plugin</a>, please tell us!</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Devoxx France 2015
<ul>
<li>Guillaume Laforge presented <a href="https://speakerdeck.com/glaforge/groovy-with-style-devoxx-2015">Groovy with Style</a></li>
<li>Cédric Champeau, Guillaume Laforge and Frédéric Simon from JFrog presented the <a href="https://speakerdeck.com/glaforge/groovys-release-process-devoxx-2015">Groovy release process</a></li>
<li><a href="https://docs.google.com/presentation/d/1llWSoa8KepAnYGFDE0HA4FtNmSdnVVHp7HPkQ5Ah94k/edit">Take control of your build with Gradle plugins</a>, presented at Devoxx France 2015 by Cédric Champeau and Eyal Lezmy</li>
</ul>
</li>
<li>Greach Conference
<ul>
<li><a href="http://fr.slideshare.net/JorgeFrancoLeza/grooscript-greach-2015">GrooScript in action</a> at Greach by Jorge Franco Leza</li>
<li>Overview of the <a href="http://fr.slideshare.net/ilopmar/greach-2015-ast-groovy-transformers-more-than-meets-the-eye">Groovy AST Transformers</a>: More than meets the eye! by Iván López at Greach 2015</li>
<li>Markus Schlichting brought <a href="http://fr.slideshare.net/madmas/documentation-brought-to-life-asciidoctor-gradle-greach-2015">documentation to life with Asciidoctor and Gradle</a></li>
<li>Burt Beckwith <a href="http://fr.slideshare.net/burtbeckwith/hacking-the-grails-spring-security-20-plugin">hacks the Grails Spring Security 2.0 plugin</a></li>
<li>Russel Winder talked about <a href="https://twitter.com/russel_winder/status/586550273855922176">GPars remoting</a> and shares his slides and associated code, at Greach Conference</li>
<li>Alonso Torres talked about <a href="http://fr.slideshare.net/alotor/greach-2015-decathlon-sport-meeting">Decathlon Sport Meeting application based on Grails</a></li>
<li><a href="http://fr.slideshare.net/fatimacasau/use-groovy-grails-in-your-spring-boot-projects">Use Groovy and Grails in your Spring Boot projects</a> by Fátima Casaú at Greach 2015</li>
<li>MrHaki shares the code of his Greach conference <a href="https://github.com/mrhaki/greach2015-grails-goodness">Grails Goodness</a> talk</li>
<li>Jeff Beck on <a href="http://beckje01.com/talks/greach-2015-cassandra-grails.html#/">Cassandra and Grails</a></li>
<li><a href="http://beckje01.com/talks/greach-2015-sec-ratpack.html#/">Securing Ratpack</a> by Jeff Beck</li>
<li>Jenn Strater on <a href="https://github.com/jlstrater/No-Nonsense-NoSQL">no nonsense NoSQL</a></li>
<li>Jenn Strater guided a discussion about <a href="https://twitter.com/JennStrater/status/587627413825318912">teaching and diversity in the Groovy community</a></li>
<li>Marco Vermeulen on <a href="http://marcovermeulen.github.io/gvm-talk/#/">GVM</a></li>
<li><a href="http://fr.slideshare.net/alotor/greach-2015-dsling-your-groovy">DSL&rsquo;ing your Groovy</a> by Alonso Torres</li>
<li><a href="http://fr.slideshare.net/aestasit/infrastructure-automation-with-gradle-and-puppet-at-greach-2015">Infrastructure automation with Gradle and Puppet</a> by Andrey Adamovich</li>
<li>Steve Pember covers <a href="http://fr.slideshare.net/StevePember/groovy-options-for-reactive-applications-greach-2015">Groovy options for reactive applications</a></li>
<li><a href="http://fr.slideshare.net/ysb33r/idiomatic-gradle-plugin-writing">Idiomatic Gradle plugin writing</a> by Schalk Cronjé at Greach 2015</li>
<li>Henrique Lobo presented about <a href="http://www.infoq.com/br/presentations/grails-alta-produtividade-em-java">how to obtain high productivity and quality with Groovy and Grails</a> (in Portuguese)</li>
<li><a href="http://www.infoq.com/presentations/tdd-cucumber-groovy-grails">Behaviour Driven Development with Cucumber, Groovy and Grails</a> by Marco Vermeulen, recorded at SpringOne2GX 2014</li>
</ul>
</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>The <a href="https://twitter.com/grailsframework/status/586263420195975169">Grails Framework is announcing having found a new home</a> at OCI</li>
<li>A <a href="https://twitter.com/greachconf/status/586806872134549504">cool Groovy T-Shirt</a> at Greach conference</li>
<li>Schalk Cronjé draws <a href="https://twitter.com/ysb33r/status/586818696737386496">GrooScript in a nutshell</a></li>
<li>Schalk Cronjé draws <a href="https://twitter.com/ysb33r/status/586843897793552384">GVM in a nutshell</a> from Marco Vermeulen Greach presentation</li>
<li>Iván López says that <a href="https://twitter.com/ilopmar/status/586827597469745153">only the &ldquo;real&rdquo; developers create AST transformations using emacs</a></li>
<li>A <a href="https://twitter.com/gvmtool/status/586892208860704768">new GVM logo in the works</a> by Alexander Klein?</li>
<li>Andrey Hihlovskiy on the <a href="https://twitter.com/AndreyHihlovski/status/587199044591714304">pain of Java&rsquo;s parentheses and semicolons</a> as the opposite of syntax sugar</li>
<li>Jonas Havers is impressed with <a href="https://twitter.com/JonasHavers/status/587245765736718336">Groovy&rsquo;s Markup Template Engine in combination with Spring Boot</a></li>
<li>The <a href="https://twitter.com/aheritier/status/587883820654944256">Groovy boys and JFrog are making the show</a> on stage at Devoxx France 2015</li>
</ul>
<h2 id="podcasts">Podcasts</h2>
<ul>
<li><a href="https://twitter.com/groovypodcast/status/586556048305213440">Groovy podcast episode 10</a> is available</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>John Engelman shares a Gradle tip on how to <a href="https://gist.github.com/johnrengelman/9a20697b2246a9bfaca2">apply a custom plugin on its own project</a></li>
<li>Tim Yates mixes the <a href="https://gist.github.com/timyates/99725e049fa9413f7e09">Document Builder and his ASCII image generator</a></li>
<li>Fernando Redondo shares the code of his Greach <a href="https://twitter.com/pronoide_fer/status/586825551907794944">Spock workshop</a></li>
<li>Bruno Borges uses Ratp<a href="https://github.com/brunoborges/nashorn/blob/master/maven/samples/ratpack/ratpack.js">ack in JavaScript with the Nashorn Maven executor</a></li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li>Object Computing is <a href="https://twitter.com/NathanTippy/status/586887533398601728">looking for Groovy developers in Saint-Louis</a>, USA</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="https://twitter.com/gr8conf/status/587841371266015232">GR8Conf Europe early bird tickets ending on Wednesday</a>, be fast!</li>
<li>A new <a href="https://twitter.com/glaforge/status/587572281511141376">Grails event in Azerbaijan</a></li>
<li>Tomas Lin collected the links to the <a href="https://fbflex.wordpress.com/2015/04/12/collection-of-links-for-greach-2015-from-twitter/">slides and code of all the Greach 2015 conference presentations</a></li>
<li>Browse the <a href="https://twitter.com/greachconf/status/587251262615334913">Greach 2015 conference pictures</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #65</title><link>https://glaforge.dev/posts/2015/04/07/groovy-weekly-65/</link><pubDate>Tue, 07 Apr 2015 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2015/04/07/groovy-weekly-65/</guid><description>&lt;p>I hope you all had a Groovy Easter, with baskets full of eggs and chocolate!&lt;/p>
&lt;p>Little update on the Groovy infrastructure, as the project moves into incubation at Apache, the &lt;a href="https://issues.apache.org/jira/browse/GROOVY/">JIRA issue tracker has moved&lt;/a>, and don’t forget that the &lt;a href="http://www.groovy-lang.org/mailing-lists.html">Groovy mailing-lists are also moving&lt;/a>.&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/arasthel92/status/584746749245423617">SwissKnife 1.2.3&lt;/a> released, the Swiss knife for for Android development with Groovy&lt;/li>
&lt;li>&lt;a href="http://ratpack.io/versions/0.9.15">Ratpack 0.9.15&lt;/a> released&lt;/li>
&lt;li>&lt;a href="https://twitter.com/MGrzejszczak/status/583390682938798080">Spock Subjects Collaborators Extension 1.0.3&lt;/a> released by Marcin Grzejszczak&lt;/li>
&lt;li>&lt;a href="https://twitter.com/grailsframework/status/583557647623913472">Grails 3.0.1&lt;/a> released&lt;/li>
&lt;li>&lt;a href="https://twitter.com/pickypg/status/585108230373830656">ElasticSearch Groovy client v1.5.0&lt;/a> released&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://jaxenter.com/grails-3-0-built-upon-spring-boot-and-gradle-116071.html">Grails 3.0 built upon Spring Boot and Gradle&lt;/a> on JaxEnter&lt;/li>
&lt;li>Using &lt;a href="http://www.objectpartners.com/2015/03/31/using-android-product-flavors-to-build-full-and-demo-version-of-the-app/">Android product flavors to build full and demo version of an application&lt;/a>, by Manij Shrestha&lt;/li>
&lt;/ul>
&lt;h2 id="news">News&lt;/h2>
&lt;ul>
&lt;li>The &lt;a href="https://issues.apache.org/jira/browse/GROOVY/?selectedTab=com.atlassian.jira.jira-projects-plugin:summary-panel">Groovy JIRA has now moved to Apache&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/Gradleware/status/583310059108712448">Gradle has a brand new forum&lt;/a> powered by Discourse&lt;/li>
&lt;li>Tim Fox shares a glimpse of the &lt;a href="http://vert-x3.github.io/">upcoming new Vert.x website&lt;/a>&lt;/li>
&lt;li>Jacob Aae Mikkelsen &lt;a href="http://grydeske.net/news/show/90">Grails Diary&lt;/a> week 14&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>SpringOne2GX 2014
&lt;ul>
&lt;li>&lt;a href="http://www.infoq.com/presentations/ratpack-grails-3">Ratpack and Grails 3&lt;/a> by Lari Hotari&lt;/li>
&lt;li>&lt;a href="http://www.infoq.com/presentations/grails-api-2014">Building awesome APIs with Grails&lt;/a> by Chris Latimer&lt;/li>
&lt;li>&lt;a href="http://www.infoq.com/presentations/gradle-android">Gradle for Android&lt;/a> by Ken Kousen&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=CKkovWazbJM">Introduction to Grails 3.0&lt;/a> by Dan Vega&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=PlMyRGIJNa0">Creating and running a Grails 3 application&lt;/a> by Dan Vega&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/grooscript/status/582180967164563456">Creating iOS applications with Groovy&lt;/a> and GrooScript, using ReactJS native&lt;/li>
&lt;li>April Fools from Apache Groovy mentor says &lt;a href="https://twitter.com/bdelacretaz/status/583192854757007360">Groovy could graduate on April 1st&lt;/a> already&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/583384389201080321">Spring Boot 1.1.12&lt;/a> available on GVM&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/583384477612797953">Spring Boot 1.2.3&lt;/a> available on GVM&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/583555987854008320">Grails 3.0.1&lt;/a> available on GVM&lt;/li>
&lt;/ul>
&lt;h2 id="mailing-list-posts">Mailing-list posts&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://groovy.329449.n5.nabble.com/ANN-JIRA-moved-to-Apache-td5723366.html">Groovy&amp;rsquo;s JIRA moved to Apache&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="books">Books&lt;/h2>
&lt;ul>
&lt;li>Peter Ledbrook shares early &lt;a href="https://twitter.com/pledbrook/status/583291238067077120">draft chapters of his Practical Groovy&lt;/a> self-published book&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/gr8confus/status/585089220848517121">GR8Conf US 2015 registration is open&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>I hope you all had a Groovy Easter, with baskets full of eggs and chocolate!</p>
<p>Little update on the Groovy infrastructure, as the project moves into incubation at Apache, the <a href="https://issues.apache.org/jira/browse/GROOVY/">JIRA issue tracker has moved</a>, and don’t forget that the <a href="http://www.groovy-lang.org/mailing-lists.html">Groovy mailing-lists are also moving</a>.</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://twitter.com/arasthel92/status/584746749245423617">SwissKnife 1.2.3</a> released, the Swiss knife for for Android development with Groovy</li>
<li><a href="http://ratpack.io/versions/0.9.15">Ratpack 0.9.15</a> released</li>
<li><a href="https://twitter.com/MGrzejszczak/status/583390682938798080">Spock Subjects Collaborators Extension 1.0.3</a> released by Marcin Grzejszczak</li>
<li><a href="https://twitter.com/grailsframework/status/583557647623913472">Grails 3.0.1</a> released</li>
<li><a href="https://twitter.com/pickypg/status/585108230373830656">ElasticSearch Groovy client v1.5.0</a> released</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li><a href="http://jaxenter.com/grails-3-0-built-upon-spring-boot-and-gradle-116071.html">Grails 3.0 built upon Spring Boot and Gradle</a> on JaxEnter</li>
<li>Using <a href="http://www.objectpartners.com/2015/03/31/using-android-product-flavors-to-build-full-and-demo-version-of-the-app/">Android product flavors to build full and demo version of an application</a>, by Manij Shrestha</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>The <a href="https://issues.apache.org/jira/browse/GROOVY/?selectedTab=com.atlassian.jira.jira-projects-plugin:summary-panel">Groovy JIRA has now moved to Apache</a></li>
<li><a href="https://twitter.com/Gradleware/status/583310059108712448">Gradle has a brand new forum</a> powered by Discourse</li>
<li>Tim Fox shares a glimpse of the <a href="http://vert-x3.github.io/">upcoming new Vert.x website</a></li>
<li>Jacob Aae Mikkelsen <a href="http://grydeske.net/news/show/90">Grails Diary</a> week 14</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>SpringOne2GX 2014
<ul>
<li><a href="http://www.infoq.com/presentations/ratpack-grails-3">Ratpack and Grails 3</a> by Lari Hotari</li>
<li><a href="http://www.infoq.com/presentations/grails-api-2014">Building awesome APIs with Grails</a> by Chris Latimer</li>
<li><a href="http://www.infoq.com/presentations/gradle-android">Gradle for Android</a> by Ken Kousen</li>
<li><a href="https://www.youtube.com/watch?v=CKkovWazbJM">Introduction to Grails 3.0</a> by Dan Vega</li>
<li><a href="https://www.youtube.com/watch?v=PlMyRGIJNa0">Creating and running a Grails 3 application</a> by Dan Vega</li>
</ul>
</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li><a href="https://twitter.com/grooscript/status/582180967164563456">Creating iOS applications with Groovy</a> and GrooScript, using ReactJS native</li>
<li>April Fools from Apache Groovy mentor says <a href="https://twitter.com/bdelacretaz/status/583192854757007360">Groovy could graduate on April 1st</a> already</li>
<li><a href="https://twitter.com/gvmtool/status/583384389201080321">Spring Boot 1.1.12</a> available on GVM</li>
<li><a href="https://twitter.com/gvmtool/status/583384477612797953">Spring Boot 1.2.3</a> available on GVM</li>
<li><a href="https://twitter.com/gvmtool/status/583555987854008320">Grails 3.0.1</a> available on GVM</li>
</ul>
<h2 id="mailing-list-posts">Mailing-list posts</h2>
<ul>
<li><a href="http://groovy.329449.n5.nabble.com/ANN-JIRA-moved-to-Apache-td5723366.html">Groovy&rsquo;s JIRA moved to Apache</a></li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>Peter Ledbrook shares early <a href="https://twitter.com/pledbrook/status/583291238067077120">draft chapters of his Practical Groovy</a> self-published book</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="https://twitter.com/gr8confus/status/585089220848517121">GR8Conf US 2015 registration is open</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #64</title><link>https://glaforge.dev/posts/2015/03/31/groovy-weekly-64/</link><pubDate>Tue, 31 Mar 2015 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2015/03/31/groovy-weekly-64/</guid><description>&lt;p>Today marks the &lt;a href="https://glaforge.dev/posts/2015/01/19/the-groovy-project-is-looking-for-a-new-home/">end of the sponsorship of the Groovy and Grails projects by Pivotal&lt;/a>. The projects are now turning a new page in their lives. For Groovy, the project is going to wear some feathers, with &lt;a href="http://www.programmableweb.com/news/groovy-project-joins-apache-software-foundation/2015/03/27">joining the Apache Software Foundation&lt;/a>. And Grails is &lt;a href="http://grails.io/post/115110650393/grails-3-0-released-and-the-road-ahead">releasing its major 3.0 version&lt;/a>.&lt;/p>
&lt;p>With Groovy’s move to Apache, it’s important to notice that the Groovy mailing-lists are moving. You &lt;a href="http://groovy.329449.n5.nabble.com/IMPORTANT-New-mailing-lists-and-JIRA-migration-td5723329.html">must subscribe to the new mailing-lists at Apache&lt;/a>, as we won’t force-subscribe people, so you’ll have to opt-in to these new lists.&lt;/p></description><content:encoded>
<![CDATA[<p>Today marks the <a href="https://glaforge.dev/posts/2015/01/19/the-groovy-project-is-looking-for-a-new-home/">end of the sponsorship of the Groovy and Grails projects by Pivotal</a>. The projects are now turning a new page in their lives. For Groovy, the project is going to wear some feathers, with <a href="http://www.programmableweb.com/news/groovy-project-joins-apache-software-foundation/2015/03/27">joining the Apache Software Foundation</a>. And Grails is <a href="http://grails.io/post/115110650393/grails-3-0-released-and-the-road-ahead">releasing its major 3.0 version</a>.</p>
<p>With Groovy’s move to Apache, it’s important to notice that the Groovy mailing-lists are moving. You <a href="http://groovy.329449.n5.nabble.com/IMPORTANT-New-mailing-lists-and-JIRA-migration-td5723329.html">must subscribe to the new mailing-lists at Apache</a>, as we won’t force-subscribe people, so you’ll have to opt-in to these new lists.</p>
<p>Last but not least, Groovy has been submitted for nomination for the JAX 2015 innovation award, so we encourage you to <a href="http://jaxenter.com/jax-awards-2015/submit-your-vote">vote for Groovy for the JAX innovation award</a>!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://github.com/grails/grails-core/releases/tag/v3.0.0">Grails 3.0</a> released</li>
<li><a href="https://twitter.com/grailsframework/status/580610565808197632">Grails 2.4.5 and 2.5.0</a> are released</li>
<li><a href="https://twitter.com/grailsframework/status/581094391521132546">Grails 3.0 RC-3</a> released, the last RC before the final release</li>
<li>Al Baker releases <a href="https://twitter.com/AlBaker_Dev/status/581168099614937088">Stardog Groovy 3.0</a></li>
<li><a href="https://twitter.com/NebulaPlugins/status/581203594021584897">Gradle Nebula-test plugin releases v2.2.1</a> with an updated Spock 1.0</li>
<li><a href="https://twitter.com/craigburke1/status/582575048764055552">Document Builder 0.3.0</a> released by Craig Burke with header / footer support</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li><a href="http://grails.io/post/115110650393/grails-3-0-released-and-the-road-ahead">Grails 3.0 released and the road ahead</a> by Graeme Rocher</li>
<li>ProgrammableWeb covers <a href="http://www.programmableweb.com/news/groovy-project-joins-apache-software-foundation/2015/03/27">Groovy project joining the Apache Software Foundation</a></li>
<li>Cédric Champeau dives into the details of an <a href="http://melix.github.io/blog/2015/03/sandboxing.html">improved sandboxing approach for Groovy scripts</a></li>
<li>Write your <a href="https://impetus-games.com/blog/libGDX-the-Groovy-Way">games in Groovy with libGDX</a></li>
<li>Marco Vermeulen details the <a href="http://www.wiredforcode.com/blog/2015/03/26/the-gvm-vendor-api/">GVM vendor API</a></li>
<li>Pavel Dudka&rsquo;s first Gradle <a href="http://trickyandroid.com/gradle-tip-1-tasks/">tip on Gradle tasks</a></li>
<li><a href="https://gradle.org/gradle-2-4-the-fastest-yet/">Gradle 2.4, the fastest yet</a>, says Luke Daley</li>
<li>Hans Dockter gives the <a href="https://gradle.org/gradle-team-perspective-on-bazel">Gradle team&rsquo;s perspective on Google&rsquo;s Bazel</a> build solution</li>
<li><a href="https://gradle.org/why/return-on-investment/">How to pitch Gradle to your boss</a>? Here&rsquo;s a recipe on Gradle&rsquo;s website</li>
<li>MrHaki&rsquo;s Groovy Goodness: <a href="http://mrhaki.blogspot.fr/2015/03/groovy-goodness-new-methods-to-sort-and.html">New methods to sort and remove duplicates from collection</a></li>
<li>Peter Ledbrook covers the infamous <a href="http://blog.cacoethes.co.uk/gradle/comments-on-recent-gradle-criticisms">Gradle halting problem</a></li>
<li>Ratpack 0.9.15 will feature <a href="https://twitter.com/ratpackweb/status/582642475388444672">non blocking health checks</a></li>
<li>Integration vs functional testing: how to <a href="http://aruizca.com/integrated-vs-functional-testing-how-to-test-rest-apis-in-grails-using-spock/">test REST APIs in Grails using Spock</a>, by Angel Ruiz</li>
<li><a href="http://devsoap.com/#!/Writing-AngularJS-applications-in-Groovy">Writing AngularJS applications in Groovy</a> by John Ahlroos</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li><a href="https://twitter.com/grailsframework/status/582466373894438912">Grails is moving away from JIRA to Github issues</a></li>
<li>The <a href="https://twitter.com/kenkousen/status/580745262148280320">Groovy Podcast now has a Twitter feed</a>, announces Ken Kousen</li>
<li>Jacob Aae Mikkelsen <a href="http://grydeske.net/news/show/89">Grails Diary</a> week 13</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>SpringOne2GX 2014
<ul>
<li><a href="http://www.infoq.com/presentations/advanced-gorm">Advanced GORM, beyond relational</a>, by Graeme Rocher</li>
<li><a href="http://www.infoq.com/presentations/grails-perf-tuning">Performance tuning Grails applications</a> by Lari Hotari</li>
<li><a href="http://www.infoq.com/presentations/groovy-metaprogramming">Runtime meta-programming with Groovy</a> by Jeff Brown</li>
<li><a href="http://www.infoq.com/presentations/groovy-spock-gradle">Groovy mobile automation</a> by Bobby Warner</li>
</ul>
</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Cédric Champeau tweets about the <a href="https://twitter.com/CedricChampeau/status/582656729600622592">new mailing-lists for the Groovy project at Apache</a>, encouraging all users to move to the new lists</li>
<li>The <a href="https://twitter.com/cedricchampeau/status/582908247692181504">new Groovy documentation is already 630 pages long in PDF</a> form, with all code snippets automatically tested, notes Cédric Champeau</li>
<li>Rob Winch and Sam Brannen are experiencing important <a href="https://twitter.com/sam_brannen/status/581884182156197888">decreased build times thanks to Gradle 2.4</a> build time improvements</li>
<li>Eugene Kamenev announces the use of Gitter for <a href="https://twitter.com/eugenekamenev/status/582548963661729792">chatting about SwissKnife and Groovy Android</a> support</li>
<li>Cédric Champeau&rsquo;s <a href="https://twitter.com/CedricChampeau/status/581129087126609920">Bytecode AST transformation supports debugging</a></li>
<li>Dan Woods notices the <a href="https://twitter.com/danveloper/status/581553702525214720">NIO support from Groovy 2.3</a></li>
<li>Dan Woods is sad to learn that the <a href="https://twitter.com/danveloper/status/581765073258987520">Groovy Grails Tool Suite is discontinued</a>, following up Pivotal&rsquo;s end of funding of the Groovy and Grails projects</li>
<li>Dierk König notes that <a href="https://twitter.com/mittie/status/581768305771450368">Groovy&rsquo;s safe navigation operator is like the Maybe monad</a>, and GPath is like a list monad</li>
<li><a href="https://twitter.com/gvmtool/status/580608623719686144">Grails 2.4.5</a> is available on GVM</li>
<li><a href="https://twitter.com/gvmtool/status/580608731085447168">Grails 2.5.0</a> is available on GVM</li>
<li><a href="https://twitter.com/gvmtool/status/581097862899113986">Grails 3.0 RC-3</a> available on GVM</li>
<li><a href="https://twitter.com/gvmtool/status/582850247929532416">Grails 3.0</a> is available on GVM</li>
<li>Craig Burke is moving his <a href="https://twitter.com/craigburke1/status/580732939371171840">document builder to OOXML</a></li>
<li>Craig Burke managed to get <a href="https://twitter.com/craigburke1/status/580788527341924352">headers working for his document builder</a></li>
<li>Al Baker says <a href="https://twitter.com/AlBaker_Dev/status/581169267619602433">Stardog Groovy 3.0 includes simplified reasoning configuration</a></li>
</ul>
<h2 id="mailing-list-posts">Mailing-list posts</h2>
<ul>
<li>With its move to the Apache foundation, Groovy is moving its mailing-lists, and <a href="http://groovy.329449.n5.nabble.com/IMPORTANT-New-mailing-lists-and-JIRA-migration-td5723329.html">subscribers of the old lists should subscribe to the new ones</a></li>
</ul>
<h2 id="podcasts">Podcasts</h2>
<ul>
<li><a href="https://twitter.com/groovypodcast/status/581107353459658752">Groovy podcast episode 9</a> is available</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Tim Yates has fun with some <a href="https://gist.github.com/timyates/7234c83d9e7df39e5a09">ASCII based image generation</a> script</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>The latest <a href="https://twitter.com/gr8conf/status/580603331707559936">newsletter about GR8Conf Europe</a> 2015</li>
<li>If you&rsquo;re wondering about <a href="https://twitter.com/gr8conf/status/580769221023399936">GR8Conf Europe, check its YouTube channel</a> to figure out!</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #63</title><link>https://glaforge.dev/posts/2015/03/24/groovy-weekly-63/</link><pubDate>Tue, 24 Mar 2015 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2015/03/24/groovy-weekly-63/</guid><description>&lt;p>The big news today is that Groovy has now &lt;a href="https://glaforge.dev/posts/2015/03/23/groovy-2-4-3-out-and-entering-apache-s-incubator/">passed the vote for entering the Apache Software Foundation incubator&lt;/a>! Thus, in the coming weeks and months, while going through the incubation process, you can expect some changes in terms of infrastructure (moving sources, issue tracker, mailng-lists, etc) to be taking place, but don’t worry, we’ll keep you informed along the way.&lt;/p>
&lt;p>This past week, we’ve had some new releases of Groovy, as well as the second release candidate for Grails 3! Grails 3 is really really close now, get ready, it’s gonna rock the house!&lt;/p></description><content:encoded>
<![CDATA[<p>The big news today is that Groovy has now <a href="https://glaforge.dev/posts/2015/03/23/groovy-2-4-3-out-and-entering-apache-s-incubator/">passed the vote for entering the Apache Software Foundation incubator</a>! Thus, in the coming weeks and months, while going through the incubation process, you can expect some changes in terms of infrastructure (moving sources, issue tracker, mailng-lists, etc) to be taking place, but don’t worry, we’ll keep you informed along the way.</p>
<p>This past week, we’ve had some new releases of Groovy, as well as the second release candidate for Grails 3! Grails 3 is really really close now, get ready, it’s gonna rock the house!</p>
<p>Alongside the Groovy releases, <a href="https://glaforge.dev/posts/2015/03/19/groovy-2-4-2-and-2-3-11-released/">big efforts have been made with the new Groovy documentation</a>. Be sure to check it out, and to help us further improve that documentation</p>
<p>Last but not least, Groovy has been submitted for nomination for the JAX 2015 innovation award, so we encourage you to <a href="http://jaxenter.com/jax-awards-2015/submit-your-vote">vote for Groovy for the JAX innovation award</a>!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://twitter.com/grailsframework/status/578127217198174208">Grails 3.0 rc-2</a> released</li>
<li><a href="https://glaforge.dev/posts/2015/03/19/groovy-2-4-2-and-2-3-11-released/">Groovy 2.4.2 and 2.3.11</a> released along with tons of new documentation</li>
<li><a href="http://groovy-lang.org/changelogs/changelog-2.4.3.html">Groovy 2.4.3</a> released</li>
<li><a href="https://twitter.com/CedricChampeau/status/580017871620874241">Gradle Android plugin for Groovy 0.3.6</a> released, working with the 1.1.0 version of the Android plugin</li>
<li><a href="https://twitter.com/CedricChampeau/status/579727386452819969">japicmp plugin for Gradle 0.1.1</a> released</li>
<li><a href="https://twitter.com/SonarSource/status/579950945263620096">SonarQube Groovy 1.1</a> released with JaCoCo coverage and latest CodeNarc 0.23</li>
<li><a href="https://www.jetbrains.com/idea/whatsnew/">IntelliJ IDEA 14.1</a> released with improved Groovy and Gradle support</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Guillaume Laforge announces the <a href="https://glaforge.dev/posts/2015/03/23/groovy-2-4-3-out-and-entering-apache-s-incubator/">release of Groovy 2.4.3 and that the vote for accepting Groovy in the Apache incubator passed</a></li>
<li>Schalk Cronjé shares his thoughts on <a href="http://delivervalue.blogspot.co.uk/2015/03/so-what-about-this-halting-problem-in.html">Gradle&rsquo;s halting problem</a></li>
<li>MrHaki&rsquo;s Groovy Goodness
<ul>
<li><a href="http://mrhaki.blogspot.fr/2015/03/groovy-goodness-use-constructor-as.html">Use constructor as method pointer</a></li>
<li><a href="http://mrhaki.blogspot.fr/2015/03/groovy-goodness-combine-elements.html">Combine elements iterable with index</a></li>
<li><a href="http://mrhaki.blogspot.fr/2015/03/groovy-goodness-swapping-elements-in.html">Swapping elements in a collection</a></li>
</ul>
</li>
<li><a href="https://solidsoft.wordpress.com/2015/03/24/automatic-promotion-of-artifacts-to-maven-central-from-gradle/">Automatic release / promotion of artifacts to Maven Central from Gradle</a> (with Gradle Nexus Staging Plugin)</li>
<li>Set up easy <a href="http://blog.comsysto.com/2015/02/20/cross-language-benchmarking-made-easy/">cross-language benchmarking using Gradle</a> by Ben Steinert</li>
<li><a href="http://wiebe-elsinga.com/blog/archive-addition-android-artifacts-with-gradle/">Archive addition Android artifacts with Gradle</a> by W.Elsinga</li>
<li><a href="https://tedvinke.wordpress.com/2015/03/15/basic-groovy-and-grails-code-review-guidelines/">Basic Groovy and Grails code review guidelines</a> by Ted Vinke</li>
<li>Using delegation for <a href="http://helpingtoomuch.blogspot.fr/2015/03/using-delegation-for-audit-columns-in.html">audit columns in Groovy on Grails</a></li>
<li>Dustin Marx on <a href="http://java.dzone.com/articles/excellent-groovy-intends-join">Groovy joining the Apache foundation</a></li>
<li><a href="https://twitter.com/bgoetzmann/status/577958849551822848">Control a presentation remotely with a Vert.x module written in Groovy</a> by Bertrand Goetzman</li>
<li>Why <a href="http://www.techgig.com/readnews.php?category=Technology%2F+Skill+News&amp;tgnews_link=http%3A%2F%2Ffeeds.dzone.com%2F~r%2Fdzone%2Ffrontpage%2F~3%2FYLKvHSdp1X0%2Fwhy_developing_in_groovygrails_is_faster_than_in.html&amp;tg_type=rss&amp;tgnews_id=54456">developing in Groovy and Grails is faster than in Java and Spring</a> asks Luigi Candita</li>
<li>Jorina Freya Gerken introduces a new series on <a href="http://eclipsesource.com/blogs/2015/03/20/spock-by-example-introducing-the-series/">Spock by example</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>You can <a href="http://jaxenter.com/jax-awards-2015/submit-your-vote">vote for Groovy for the JAX 2015 innovation award</a></li>
<li>The <a href="http://www.gradle.org/">Gradle website has been redesigned</a></li>
<li>Jacob Aae Mikkelsen <a href="https://twitter.com/JacobAae/status/580150710886645760">Grails Diary</a> week 12</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Iván López published his code and slides on <a href="https://twitter.com/ilopmar/status/578121164632936450">Grails and the realtime world</a></li>
<li>Video of the <a href="https://twitter.com/andrewreitz_/status/580337018493775873">Getting Groovy on Android</a> presentation by Andrew Reitz</li>
<li>Peter Ledbrook gave a tutorial on <a href="https://www.parleys.com/talk/groovy-java-developers">Groovy for Java developers</a> at Devoxx UK 2014</li>
<li>SpringOne2GX 2014
<ul>
<li>
<p><a href="http://www.infoq.com/presentations/groovy-ast">Groovy AST transformations</a> by Paul King</p>
</li>
<li>
<p><a href="http://www.infoq.com/presentations/groovy-distributed-enterprise">Distributed Platform Development with Groovy</a> by Dan Woods</p>
</li>
<li>
<p><a href="http://www.infoq.com/presentations/groovy-tips-tricks">Advanced Groovy Tips and Tricks</a> by Ken Kousen</p>
</li>
<li>
<p><a href="http://www.infoq.com/presentations/groovy-gorm">GORM Inside and out</a> by Jeff Scott Brown</p>
</li>
</ul>
</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Bertrand Delacretaz, mentor of the Groovy project, announces the <a href="https://twitter.com/bdelacretaz/status/580057703202086912">vote passed to accept Groovy in the Apache Software Foundation incubator</a></li>
<li>Cédric Champeau updated his <a href="https://twitter.com/CedricChampeau/status/580479826563362817">@Bytecode AST transformation</a>, but he warns about using it for educational purpose only</li>
<li>Baruch Sadogursky claims that <a href="https://twitter.com/jbaruch/status/580269909520752640">Maven&rsquo;s biggest problem is not XML so polyglotism won&rsquo;t help it</a></li>
<li><a href="https://twitter.com/gvmtool/status/580071538805862400">Groovy 2.4.3</a> available on GVM</li>
<li><a href="https://twitter.com/gvmtool/status/579688895304085504">Groovy 2.4.2</a> available on GVM</li>
<li><a href="https://twitter.com/gvmtool/status/579688160973713408">Groovy 2.3.11</a> available on GVM</li>
<li>Creating <a href="https://twitter.com/PSJedox/status/578671464074752003/photo/1">CUDA code with Groovy scripts</a></li>
<li>Bavo Bruylandt says that the <a href="https://twitter.com/bavobbr/status/578807970886209537">@ClosureParams and @DelegatesTo annotations greatly help IDE integration for Groovy DSLs</a></li>
<li>Ben McGuire noted that most NFJS attendees were using Groovy in some capacity, showing an <a href="https://twitter.com/ben_t_mcguire/status/579399082029944832">ongoing growth of the Groovy community</a></li>
<li>Oliver Gierke likes the <a href="https://twitter.com/olivergierke/status/577903566070902785">Groovy based poms with Maven 3.3.1</a></li>
<li>Oliver Gierke ported the Spring HATEOAS <a href="https://twitter.com/olivergierke/status/578521532349157376">pom.xml to Groovy with Maven&rsquo;s polyglot</a> support</li>
<li><a href="https://twitter.com/grailsframework/status/577843537284325378">Third party plugins compatible with Grails 3</a> are becoming available</li>
<li>A <a href="https://twitter.com/FlywayDb/status/577872090998575108">Flyway Gradle plugin</a> appears into the Gradle plugin portal</li>
<li><a href="https://twitter.com/gvmtool/status/578129119361306624">Grails 3.0 rc-2</a> available on GVM</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li><a href="https://twitter.com/ManningMEAP/status/580416001814204417">Groovy in Action, 2nd edition</a>, in the finish line!</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="https://twitter.com/gr8conf/status/580435505852071936">Tickets for GR8Conf Europe</a> now on sale!</li>
<li><a href="https://twitter.com/gr8conf/status/577996465248718848">GR8Conf Europe 2015 is available on Lanyrd</a>, you can mark you&rsquo;re attending</li>
<li><a href="https://twitter.com/greachconf/status/579609854320381952">Two Java Champions will be speaking at the Greach</a> conference, with Andrés Almiray and Trisha Gee</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.4.3 out and entering Apache's incubator</title><link>https://glaforge.dev/posts/2015/03/23/groovy-2-4-3-out-and-entering-apache-s-incubator/</link><pubDate>Mon, 23 Mar 2015 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2015/03/23/groovy-2-4-3-out-and-entering-apache-s-incubator/</guid><description>&lt;p>The Groovy team is happy to announce another 2.4 release, with Groovy 2.4.3.&lt;/p>
&lt;p>We noticed some regressions that needed our attention, and we decided to release rapidly after our recent 2.4.2 release of last week. Please upgrade to 2.4.3 if you&amp;rsquo;re on the 2.4.x line.&lt;/p>
&lt;p>You can read about the tickets resolved in our changelog: &lt;a href="http://groovy-lang.org/changelogs/changelog-2.4.3.html">http://groovy-lang.org/changelogs/changelog-2.4.3.html&lt;/a>&lt;/p>
&lt;p>And you can download the &lt;a href="http://www.groovy-lang.org/download.html">Groovy distribution&lt;/a> as usual on the new Groovy website.&lt;/p>
&lt;p>I&amp;rsquo;d also like to announce that Groovy &lt;a href="http://markmail.org/message/uaickpdsffd4gnzu">passed the vote to enter the Apache Software Foundation Incubator&lt;/a>! In the coming weeks, we&amp;rsquo;ll be through the incubation process, likely for a few months, where we&amp;rsquo;ll be moving things around (sources, JIRA, mailing-lists, etc), so you can expect a bit of change, but we&amp;rsquo;ll keep you posted on how we progress along.&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy team is happy to announce another 2.4 release, with Groovy 2.4.3.</p>
<p>We noticed some regressions that needed our attention, and we decided to release rapidly after our recent 2.4.2 release of last week. Please upgrade to 2.4.3 if you&rsquo;re on the 2.4.x line.</p>
<p>You can read about the tickets resolved in our changelog: <a href="http://groovy-lang.org/changelogs/changelog-2.4.3.html">http://groovy-lang.org/changelogs/changelog-2.4.3.html</a></p>
<p>And you can download the <a href="http://www.groovy-lang.org/download.html">Groovy distribution</a> as usual on the new Groovy website.</p>
<p>I&rsquo;d also like to announce that Groovy <a href="http://markmail.org/message/uaickpdsffd4gnzu">passed the vote to enter the Apache Software Foundation Incubator</a>! In the coming weeks, we&rsquo;ll be through the incubation process, likely for a few months, where we&rsquo;ll be moving things around (sources, JIRA, mailing-lists, etc), so you can expect a bit of change, but we&rsquo;ll keep you posted on how we progress along.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.4.2 and 2.3.11 released</title><link>https://glaforge.dev/posts/2015/03/19/groovy-2-4-2-and-2-3-11-released/</link><pubDate>Thu, 19 Mar 2015 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2015/03/19/groovy-2-4-2-and-2-3-11-released/</guid><description>&lt;p>The Groovy development team is happy to announce the bug fix releases of Groovy 2.4.2 and Groovy 2.3.11.&lt;/p>
&lt;p>For the 2.4.2 release, some &lt;strong>key performance improvements have found their way in static compilation mode&lt;/strong>, so you might be happy to see your programs snappier!&lt;/p>
&lt;p>Along with those two releases, you&amp;rsquo;ll notice &lt;strong>lots more documentation has been written&lt;/strong> on the new Groovy website, as well as important updates to existing topics. You might want to have a look at the following new or improved sections:&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy development team is happy to announce the bug fix releases of Groovy 2.4.2 and Groovy 2.3.11.</p>
<p>For the 2.4.2 release, some <strong>key performance improvements have found their way in static compilation mode</strong>, so you might be happy to see your programs snappier!</p>
<p>Along with those two releases, you&rsquo;ll notice <strong>lots more documentation has been written</strong> on the new Groovy website, as well as important updates to existing topics. You might want to have a look at the following new or improved sections:</p>
<ul>
<li>
<p><a href="http://docs.groovy-lang.org/latest/html/documentation/core-domain-specific-languages.html">Domain-Specific Languages</a></p>
</li>
<li>
<p>core language</p>
<ul>
<li><a href="http://docs.groovy-lang.org/docs/latest/html/documentation/#_scripts_versus_classes">scripts vs classes</a></li>
<li><a href="http://docs.groovy-lang.org/docs/latest/html/documentation/#_fields_and_properties">fields and properties</a></li>
<li><a href="http://docs.groovy-lang.org/docs/latest/html/documentation/#_interfaces">interfaces</a></li>
<li><a href="http://docs.groovy-lang.org/docs/latest/html/documentation/#_labeled_statements">labeled statements</a></li>
<li><a href="http://docs.groovy-lang.org/latest/html/documentation/core-traits.html">traits</a></li>
</ul>
</li>
<li>
<p>annotations</p>
<ul>
<li><a href="http://docs.groovy-lang.org/docs/latest/html/documentation/#_meta_annotations">meta-annotations</a></li>
<li><a href="http://docs.groovy-lang.org/docs/latest/html/documentation/#_closure_annotation_parameters">closure annotation parameters</a></li>
</ul>
</li>
<li>
<p>AST</p>
<ul>
<li><a href="http://docs.groovy-lang.org/docs/latest/html/documentation/#_testing_ast_transformations">testing AST transformations</a></li>
<li><a href="http://docs.groovy-lang.org/docs/latest/html/documentation/#_ast_api_guide">AST API guide</a></li>
<li><a href="http://docs.groovy-lang.org/docs/latest/html/documentation/#transforms-global">global AST transformations</a></li>
<li><a href="http://docs.groovy-lang.org/docs/latest/html/documentation/#transforms-local">local AST transformations</a></li>
</ul>
</li>
<li>
<p><a href="http://docs.groovy-lang.org/docs/latest/html/documentation/#_power_assertions">power assertions</a></p>
</li>
<li>
<p><a href="http://docs.groovy-lang.org/docs/latest/html/documentation/#_script_base_classes">base script classes</a></p>
</li>
<li>
<p><a href="http://docs.groovy-lang.org/latest/html/documentation/type-checking-extensions.html">type checking extensions</a></p>
</li>
<li>
<p><a href="http://docs.groovy-lang.org/latest/html/documentation/template-engines.html">template engines</a></p>
</li>
<li>
<p><a href="http://docs.groovy-lang.org/latest/html/documentation/markup-template-engine.html">markup template engine</a></p>
</li>
<li>
<p>builders</p>
<ul>
<li><a href="http://docs.groovy-lang.org/docs/latest/html/documentation/#_antbuilder">Ant builder</a></li>
<li><a href="http://docs.groovy-lang.org/docs/latest/html/documentation/#_objectgraphbuilder">object graph builder</a></li>
</ul>
</li>
<li>
<p>guides</p>
<ul>
<li><a href="http://docs.groovy-lang.org/latest/html/documentation/servlet-userguide.html">servlet user guide</a></li>
<li><a href="http://docs.groovy-lang.org/latest/html/documentation/working-with-collections.html">working with collections</a></li>
<li><a href="http://docs.groovy-lang.org/latest/html/documentation/working-with-io.html">working with I/O</a></li>
<li><a href="http://docs.groovy-lang.org/latest/html/documentation/core-testing-guide.html">testing guide</a></li>
</ul>
</li>
</ul>
<p>Note that it might be the last releases under the Codehaus era, as Groovy is <strong>currently being voted for incubation at the Apache Software Foundation</strong>. The vote is ongoing right now, as I write those lines, so we should know pretty soon what&rsquo;s the outcome, and if Groovy is accepted or not! So far&hellip; only positive votes!</p>
<p>You can:</p>
<ul>
<li><strong>download</strong> those latest Groovy releases in our <a href="http://www.groovy-lang.org/download.html">download area</a></li>
<li>read the <strong>release notes</strong> for</li>
<li><a href="http://www.groovy-lang.org/changelogs/changelog-2.4.2.html">Groovy 2.4.2</a></li>
<li><a href="http://www.groovy-lang.org/changelogs/changelog-2.3.11.html">Groovy 2.3.11</a></li>
</ul>
<p>Thanks a lot to all who contributed to those releases, and thanks for your support of Groovy and your positive feedback on our move to Apache!</p>
<p>Keep on groovy&rsquo;ing!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #62</title><link>https://glaforge.dev/posts/2015/03/17/groovy-weekly-62/</link><pubDate>Tue, 17 Mar 2015 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2015/03/17/groovy-weekly-62/</guid><description>&lt;p>Following-up last week’s &lt;a href="https://glaforge.dev/posts/2015/03/04/groovy-projects-intends-to-join-the-apache-software-foundation/">decision of the Groovy team to join the Apache Software Foundation&lt;/a>, this week, a &lt;a href="https://wiki.apache.org/incubator/GroovyProposal">proposal&lt;/a> has been made and submitted, and Apache has &lt;a href="https://blogs.apache.org/foundation/entry/groovy_submitted_to_become_a">announced it on its blog&lt;/a>. So the process is now really officially started and on its track!&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/grailsframework/status/575995370376949760">Grails 3.0 RC-1&lt;/a> released&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/575689514317975552">GVM 2.4.0&lt;/a> released with broadcast and offline checks optimizations&lt;/li>
&lt;li>&lt;a href="https://spring.io/blog/2015/03/11/spring-tool-suite-and-groovy-grails-tool-suite-3-6-4-released">Groovy Grails Toolsuite 3.6.4&lt;/a> released with Groovy 2.4 support&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>The Groovy team and Apache members collaborated on a &lt;a href="https://wiki.apache.org/incubator/GroovyProposal">proposal to join the Apache Software Foundation&lt;/a>&lt;/li>
&lt;li>Matt Raible &lt;a href="http://www.infoq.com/news/2015/03/groovy-moving-to-apache">interviewed Guillaume Laforge about Groovy moving to Apache&lt;/a>&lt;/li>
&lt;li>Darryl Taft from &lt;a href="http://www.eweek.com/developer/groovy-language-proposed-as-apache-incubator-project.html">eWeek is covering the proposal of Groovy to join the Apache&lt;/a> incubator&lt;/li>
&lt;li>Paul Krill writes on CIO &amp;ldquo;&lt;a href="http://www.cio.com.au/article/570225/dumped-by-pivotal-groovy-moves-apache/">dumped by Pivotal, Groovy moves to Apache&lt;/a>&amp;rdquo;&lt;/li>
&lt;li>Kay Ewbank on &lt;a href="http://www.i-programmer.info/news/136-open-source/8378-groovy-joins-apache.html">Groovy joining the Apache Software Foundation&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/Shmoolki/status/576818810587611136">Groovy hands-on&lt;/a>, in French&lt;/li>
&lt;li>A new &lt;a href="http://new.griffon-framework.org/tutorials/4_javafx_views.html">Griffon / JavaFX tutorial&lt;/a> is available&lt;/li>
&lt;li>A &lt;a href="http://wiki.apidesign.org/wiki/Gradle">flamewar against Gradle&lt;/a> which is said to suffer from the halting problem&lt;/li>
&lt;li>Allegro Tech is sharing its &lt;a href="http://allegrotech.io/Adopting-Gradle-at-allegro-pl-a-success-story.html">success story adopting Gradle&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="news">News&lt;/h2>
&lt;ul>
&lt;li>The Apache Software Foundation announces &lt;a href="https://blogs.apache.org/foundation/entry/groovy_submitted_to_become_a">Groovy&amp;rsquo;s submission to become an official Apache project&lt;/a>&lt;/li>
&lt;li>At the Adobe Summit, &lt;a href="https://github.com/TWCable/cq-gradle-plugin">Time Warner Cable announced some of its Gradle plugins&lt;/a> to work with Adobe tools&lt;/li>
&lt;li>A very interesting contribution from JetBrain&amp;rsquo;s Peter Gromov has been merged into Groovy&amp;rsquo;s master to &lt;a href="https://github.com/groovy/groovy-core/pull/552">speedup compilation&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/andyjduncan/opencsv-iterator">OpenCSVIterator&lt;/a>: a useful library for processing flat text files in Groovy and Java&lt;/li>
&lt;li>GR8CRM, a suite of &lt;a href="http://gr8crm.github.io/">Grails plugins for Customer Relationship Management&lt;/a> by Göran Ehrsson&lt;/li>
&lt;li>A nice online &lt;a href="https://groovy-playground.appspot.com/">Groovy playground&lt;/a> by Tim Roes&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://groovy-the-superb-homeless.mally.pl/#/">Great introduction to Groovy&lt;/a> by Michał Mally at the Groovy user group in Poland&lt;/li>
&lt;li>&lt;a href="http://www.infoq.com/presentations/groovy-for-java">Groovy for Java developers&lt;/a>, by Peter Ledbrook, recorded during SpringOne2GX 2014&lt;/li>
&lt;li>&lt;a href="http://www.infoq.com/presentations/geb">Groovy browser automation with Geb&lt;/a>, by Colin Harrington, recorded at SpringOne2GX 2014&lt;/li>
&lt;li>A video introduction to &lt;a href="https://www.youtube.com/watch?v=cD7NPxuuXYY">Gradle for Android Studio&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau is annoyed when &lt;a href="https://twitter.com/CedricChampeau/status/576062386085433345">people reduce Groovy to just a &amp;ldquo;dynamic&amp;rdquo; language when it&amp;rsquo;s much more&lt;/a> than that&lt;/li>
&lt;li>Ken Kousen tasting a &lt;a href="https://twitter.com/kenkousen/status/576904271163232257">Groovy Smoothie&lt;/a>&lt;/li>
&lt;li>Matthew McCullough &lt;a href="https://twitter.com/matthewmccull/status/576062271924891649">Groovy JMeter plugin is moving to Github&lt;/a>, like the many Google Code projects following Google&amp;rsquo;s closing&lt;/li>
&lt;li>&lt;a href="https://twitter.com/grooscript/status/576858409678540800">GrooScript plugin working for Grails 3 RC-1&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="mailing-list-posts">Mailing-list posts&lt;/h2>
&lt;ul>
&lt;li>The &lt;a href="http://mail-archives.apache.org/mod_mbox/incubator-general/201503.mbox/%3CCA+ULb+tHEc5N9vDvu526Jmb=te4F3yCwCWWJxxMC07EAz_35wQ@mail.gmail.com%3E">Groovy incubation proposal is being discussed on the Apache&lt;/a> mailing-lists&lt;/li>
&lt;/ul>
&lt;h2 id="podcasts">Podcasts&lt;/h2>
&lt;ul>
&lt;li>Ken Kousen published &lt;a href="https://twitter.com/kenkousen/status/576067688931454977">Groovy Podcast episode 8&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>Using &lt;a href="https://gist.github.com/chiquitinxx/ef2d33ce9d1a810ba216">RxJs from GrooScript with Groovy&amp;rsquo;s @BaseScript&lt;/a> transformation&lt;/li>
&lt;/ul>
&lt;h2 id="books">Books&lt;/h2>
&lt;ul>
&lt;li>You can find Ken Kousen&amp;rsquo;s &lt;a href="https://twitter.com/manningbooks/status/577487522990714881">Making Java Groovy book code samples&lt;/a> on Manning&amp;rsquo;s website&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/springone2gx/status/576765439348318208">Registration for SpringOne2GX&lt;/a> 2015 is open&lt;/li>
&lt;li>Guillaume Laforge will be speaking about &lt;a href="https://twitter.com/jfrog/status/576052612144885762">Groovy&amp;rsquo;s continuous delivery at the SwampUp conference&lt;/a>&lt;/li>
&lt;li>At the Gradle Summit, &lt;a href="https://twitter.com/gradleware/status/577527498281472000">Netflix&amp;rsquo; build team will share lessons learned about Gradle plugins&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>Following-up last week’s <a href="https://glaforge.dev/posts/2015/03/04/groovy-projects-intends-to-join-the-apache-software-foundation/">decision of the Groovy team to join the Apache Software Foundation</a>, this week, a <a href="https://wiki.apache.org/incubator/GroovyProposal">proposal</a> has been made and submitted, and Apache has <a href="https://blogs.apache.org/foundation/entry/groovy_submitted_to_become_a">announced it on its blog</a>. So the process is now really officially started and on its track!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://twitter.com/grailsframework/status/575995370376949760">Grails 3.0 RC-1</a> released</li>
<li><a href="https://twitter.com/gvmtool/status/575689514317975552">GVM 2.4.0</a> released with broadcast and offline checks optimizations</li>
<li><a href="https://spring.io/blog/2015/03/11/spring-tool-suite-and-groovy-grails-tool-suite-3-6-4-released">Groovy Grails Toolsuite 3.6.4</a> released with Groovy 2.4 support</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>The Groovy team and Apache members collaborated on a <a href="https://wiki.apache.org/incubator/GroovyProposal">proposal to join the Apache Software Foundation</a></li>
<li>Matt Raible <a href="http://www.infoq.com/news/2015/03/groovy-moving-to-apache">interviewed Guillaume Laforge about Groovy moving to Apache</a></li>
<li>Darryl Taft from <a href="http://www.eweek.com/developer/groovy-language-proposed-as-apache-incubator-project.html">eWeek is covering the proposal of Groovy to join the Apache</a> incubator</li>
<li>Paul Krill writes on CIO &ldquo;<a href="http://www.cio.com.au/article/570225/dumped-by-pivotal-groovy-moves-apache/">dumped by Pivotal, Groovy moves to Apache</a>&rdquo;</li>
<li>Kay Ewbank on <a href="http://www.i-programmer.info/news/136-open-source/8378-groovy-joins-apache.html">Groovy joining the Apache Software Foundation</a></li>
<li><a href="https://twitter.com/Shmoolki/status/576818810587611136">Groovy hands-on</a>, in French</li>
<li>A new <a href="http://new.griffon-framework.org/tutorials/4_javafx_views.html">Griffon / JavaFX tutorial</a> is available</li>
<li>A <a href="http://wiki.apidesign.org/wiki/Gradle">flamewar against Gradle</a> which is said to suffer from the halting problem</li>
<li>Allegro Tech is sharing its <a href="http://allegrotech.io/Adopting-Gradle-at-allegro-pl-a-success-story.html">success story adopting Gradle</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>The Apache Software Foundation announces <a href="https://blogs.apache.org/foundation/entry/groovy_submitted_to_become_a">Groovy&rsquo;s submission to become an official Apache project</a></li>
<li>At the Adobe Summit, <a href="https://github.com/TWCable/cq-gradle-plugin">Time Warner Cable announced some of its Gradle plugins</a> to work with Adobe tools</li>
<li>A very interesting contribution from JetBrain&rsquo;s Peter Gromov has been merged into Groovy&rsquo;s master to <a href="https://github.com/groovy/groovy-core/pull/552">speedup compilation</a></li>
<li><a href="https://github.com/andyjduncan/opencsv-iterator">OpenCSVIterator</a>: a useful library for processing flat text files in Groovy and Java</li>
<li>GR8CRM, a suite of <a href="http://gr8crm.github.io/">Grails plugins for Customer Relationship Management</a> by Göran Ehrsson</li>
<li>A nice online <a href="https://groovy-playground.appspot.com/">Groovy playground</a> by Tim Roes</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li><a href="http://groovy-the-superb-homeless.mally.pl/#/">Great introduction to Groovy</a> by Michał Mally at the Groovy user group in Poland</li>
<li><a href="http://www.infoq.com/presentations/groovy-for-java">Groovy for Java developers</a>, by Peter Ledbrook, recorded during SpringOne2GX 2014</li>
<li><a href="http://www.infoq.com/presentations/geb">Groovy browser automation with Geb</a>, by Colin Harrington, recorded at SpringOne2GX 2014</li>
<li>A video introduction to <a href="https://www.youtube.com/watch?v=cD7NPxuuXYY">Gradle for Android Studio</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Cédric Champeau is annoyed when <a href="https://twitter.com/CedricChampeau/status/576062386085433345">people reduce Groovy to just a &ldquo;dynamic&rdquo; language when it&rsquo;s much more</a> than that</li>
<li>Ken Kousen tasting a <a href="https://twitter.com/kenkousen/status/576904271163232257">Groovy Smoothie</a></li>
<li>Matthew McCullough <a href="https://twitter.com/matthewmccull/status/576062271924891649">Groovy JMeter plugin is moving to Github</a>, like the many Google Code projects following Google&rsquo;s closing</li>
<li><a href="https://twitter.com/grooscript/status/576858409678540800">GrooScript plugin working for Grails 3 RC-1</a></li>
</ul>
<h2 id="mailing-list-posts">Mailing-list posts</h2>
<ul>
<li>The <a href="http://mail-archives.apache.org/mod_mbox/incubator-general/201503.mbox/%3CCA+ULb+tHEc5N9vDvu526Jmb=te4F3yCwCWWJxxMC07EAz_35wQ@mail.gmail.com%3E">Groovy incubation proposal is being discussed on the Apache</a> mailing-lists</li>
</ul>
<h2 id="podcasts">Podcasts</h2>
<ul>
<li>Ken Kousen published <a href="https://twitter.com/kenkousen/status/576067688931454977">Groovy Podcast episode 8</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Using <a href="https://gist.github.com/chiquitinxx/ef2d33ce9d1a810ba216">RxJs from GrooScript with Groovy&rsquo;s @BaseScript</a> transformation</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>You can find Ken Kousen&rsquo;s <a href="https://twitter.com/manningbooks/status/577487522990714881">Making Java Groovy book code samples</a> on Manning&rsquo;s website</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="https://twitter.com/springone2gx/status/576765439348318208">Registration for SpringOne2GX</a> 2015 is open</li>
<li>Guillaume Laforge will be speaking about <a href="https://twitter.com/jfrog/status/576052612144885762">Groovy&rsquo;s continuous delivery at the SwampUp conference</a></li>
<li>At the Gradle Summit, <a href="https://twitter.com/gradleware/status/577527498281472000">Netflix&rsquo; build team will share lessons learned about Gradle plugins</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #61</title><link>https://glaforge.dev/posts/2015/03/10/groovy-weekly-61/</link><pubDate>Tue, 10 Mar 2015 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2015/03/10/groovy-weekly-61/</guid><description>&lt;p>Big news for this week with the intention of the &lt;a href="https://glaforge.dev/posts/2015/03/04/groovy-projects-intends-to-join-the-apache-software-foundation/">Groovy project to join the Apache Software Foundation&lt;/a>! And if you wnat to know more about who actually &lt;a href="http://melix.github.io/blog/2015/02/who-is-groovy.html">contributed to Groovy over the years&lt;/a>, Cédric detailed that on his blog.&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://gmavenplus.56682.x6.nabble.com/gmavenplus-announce-GMavenPlus-1-5-Released-td222.html">GMavenPlus 1.5&lt;/a> released&lt;/li>
&lt;li>&lt;a href="http://grooscript.org/changes.html">GrooScript 1.0.1&lt;/a> released&lt;/li>
&lt;li>&lt;a href="https://twitter.com/grooscript/status/572876188902813697">GrooScript Gradle plugin 1.0.1&lt;/a> released&lt;/li>
&lt;li>&lt;a href="https://twitter.com/andrewreitz_/status/575138522077659136">Android-Spock 1.2&lt;/a> released working with Spock 1.0&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/574982347768119296">GVM 2.3.0&lt;/a> released with new “outdated” command to know if newer candidates are available&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>Guillaume Laforge announces the intention of the &lt;a href="https://glaforge.dev/posts/2015/03/04/groovy-projects-intends-to-join-the-apache-software-foundation/">Groovy project to join the Apache Software Foundation&lt;/a>&lt;/li>
&lt;li>Cédric Champeau wrote a very interesting and insightful &lt;a href="http://melix.github.io/blog/2015/02/who-is-groovy.html">analysis on who contributed to Groovy&lt;/a> over the years&lt;/li>
&lt;li>John K. Waters from ADTMag on &lt;a href="http://adtmag.com/articles/2015/03/04/groovy-joins-apache-foundation.aspx">Groovy to join Apache Software Foundation&lt;/a> following up an interview with Guillaume Laforge&lt;/li>
&lt;li>Jochen Theodorou brainstorms on the path &lt;a href="http://blackdragsview.blogspot.fr/2015/03/thoughts-about-new-meta-class-system.html">towards a revamped MOP for Groovy&lt;/a>&lt;/li>
&lt;li>MrHaki&amp;rsquo;s Awesome Asciidoctor: &lt;a href="http://mrhaki.blogspot.fr/2015/03/awesome-asciidoctor-use-inline.html">Use inline extension DSL with Gradle&lt;/a>&lt;/li>
&lt;li>MrHaki&amp;rsquo;s Gradle Goodness: &lt;a href="http://mrhaki.blogspot.fr/2015/03/gradle-goodness-define-system.html">Define system properties in gradle.properties&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://blog.jetbrains.com/idea/2015/03/new-features-for-gradle-coming-in-intellij-idea-14-1/">New features for Gradle Coming in IntelliJ IDEA 14.1&lt;/a> by Andrey Cheptsov&lt;/li>
&lt;li>&lt;a href="http://grooscript.org/react_example.html">Groovy, GrooScript with React.JS&lt;/a>&lt;/li>
&lt;li>Marcin Zajączkowski on &lt;a href="https://solidsoft.wordpress.com/2015/03/09/spock-1-0-with-groovy-2-4-configuration-comparison-in-maven-and-gradle/">Spock 1.0 with Groovy 2.4 configuration comparison in Maven and Gradle&lt;/a>&lt;/li>
&lt;li>Benoît Hédiard writes about how to &lt;a href="https://medium.com/@benorama/how-to-publish-your-grails-3-plugin-to-bintray-c341b24f567d">publish your Grails 3 plugin to Bintray&lt;/a>&lt;/li>
&lt;li>The Ratpack team published more information on &lt;a href="http://www.ratpack.io/manual/0.9.15/intro.html#about_this_documentation">how the Ratpack documentation is working&lt;/a> and how code samples are tested&lt;/li>
&lt;li>Ken Kousen in &amp;ldquo;&lt;a href="https://kousenit.wordpress.com/2015/03/10/if-a-method-arg-is-a-closure-for-crying-out-loud-pass-it-a-closure/">If a method arg is a closure, for crying out loud pass it a closure&lt;/a>&amp;rdquo;&lt;/li>
&lt;li>&lt;a href="http://blog.jetbrains.com/idea/2015/03/faster-groovy-compilation-in-intellij-idea-14-1/">Faster Groovy Compilation in IntelliJ IDEA&lt;/a> 14.1 by Peter Gromov&lt;/li>
&lt;li>Grails tip: &lt;a href="http://blog.jdriven.com/2015/03/grails-tip-rendering-errors-of-multiple-beans/">Rendering errors of multiple beans&lt;/a> by Albert van Veen&lt;/li>
&lt;li>Get Groovy with &lt;a href="http://zeroturnaround.com/blog/get-groovy-with-jrebel-and-grails/">JRebel and Grails&lt;/a> by Adam Koblentz&lt;/li>
&lt;li>Working with &lt;a href="http://www.intelligrape.com/blog/working-with-sass-scripts-in-grails-2-3-x/">SASS scripts in Grails 2.3&lt;/a> by Mansi Arora&lt;/li>
&lt;li>&lt;a href="http://www.objectpartners.com/2015/03/03/customize-your-grails-test-reports/">Customize your Grails test reports&lt;/a> by Igor Shults&lt;/li>
&lt;li>&lt;a href="http://www.objectpartners.com/2015/03/06/another-method-for-admin-screens-with-grails/">Another method for admin screens with Grails&lt;/a> by Mike Hostetler&lt;/li>
&lt;/ul>
&lt;h2 id="news">News&lt;/h2>
&lt;ul>
&lt;li>The &lt;a href="https://twitter.com/marc0der/status/573405378744098817">GVM project just reached 400 stars on Github&lt;/a>, keep star&amp;rsquo;ing!&lt;/li>
&lt;li>New &lt;a href="http://docs.groovy-lang.org/next/html/documentation/core-object-orientation.html#_meta_annotations">documentation on Groovy meta-annotations&lt;/a> authored by Cédric Champeau&lt;/li>
&lt;li>Kunal Dabir created a simple &lt;a href="https://github.com/kdabir/directree">DSL to create directory trees and files&lt;/a>&lt;/li>
&lt;li>An updated &lt;a href="http://grooscript.org/roadmap.html">roadmap for GrooScript&lt;/a>, including support for require.js&lt;/li>
&lt;li>Jacob Aae Mikkelsen&amp;rsquo;s &lt;a href="http://grydeske.net/news/show/86">Grails Diary&lt;/a> week 10 of 2015&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>Thanks to Luke Daley and Will Erickson, &lt;a href="https://twitter.com/CedricChampeau/status/573763143593295872">Dagger 2 will work out of the box with Groovy sources and Gradle 2.4&lt;/a>&lt;/li>
&lt;li>Cédric Champeau documented how you can &lt;a href="https://twitter.com/CedricChampeau/status/573583638127845376">use closures as annotation parameters&lt;/a>&lt;/li>
&lt;li>Cédric Champeau is so much into AST transformations that he wrongly coined the &lt;a href="https://twitter.com/CedricChampeau/status/573522774758662144">Apache Software Foundation with the AST acronym&lt;/a> instead of the ASF&lt;/li>
&lt;li>Using &lt;a href="https://twitter.com/davidthecoder/status/574223398945091584">Reactor with Groovy is a nice experience in data processing&lt;/a> says David Dawson&lt;/li>
&lt;li>Dan Woods reminds us that &lt;a href="https://twitter.com/danveloper/status/574298666623614976">Ratpack has a forum&lt;/a> in case you need help getting started&lt;/li>
&lt;li>Tim Yates found some &lt;a href="https://twitter.com/tim_yates/status/574270524139945984">Groovy biscuits&lt;/a> for the discerning Groovy developer&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>Using &lt;a href="https://gist.github.com/chiquitinxx/b01d0696cfabcc34527b">RxJs with GrooScript&lt;/a>&lt;/li>
&lt;li>Iván López authored a handy Groovy &lt;a href="https://gist.github.com/lmivan/a752644419fb8d2f2221">script to generate the HTML of the Greach conference speakers pages&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>GR8Conf Europe 2015 announces a &lt;a href="http://gr8conf.eu/#/">first batch of speakers&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>Big news for this week with the intention of the <a href="https://glaforge.dev/posts/2015/03/04/groovy-projects-intends-to-join-the-apache-software-foundation/">Groovy project to join the Apache Software Foundation</a>! And if you wnat to know more about who actually <a href="http://melix.github.io/blog/2015/02/who-is-groovy.html">contributed to Groovy over the years</a>, Cédric detailed that on his blog.</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://gmavenplus.56682.x6.nabble.com/gmavenplus-announce-GMavenPlus-1-5-Released-td222.html">GMavenPlus 1.5</a> released</li>
<li><a href="http://grooscript.org/changes.html">GrooScript 1.0.1</a> released</li>
<li><a href="https://twitter.com/grooscript/status/572876188902813697">GrooScript Gradle plugin 1.0.1</a> released</li>
<li><a href="https://twitter.com/andrewreitz_/status/575138522077659136">Android-Spock 1.2</a> released working with Spock 1.0</li>
<li><a href="https://twitter.com/gvmtool/status/574982347768119296">GVM 2.3.0</a> released with new “outdated” command to know if newer candidates are available</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Guillaume Laforge announces the intention of the <a href="https://glaforge.dev/posts/2015/03/04/groovy-projects-intends-to-join-the-apache-software-foundation/">Groovy project to join the Apache Software Foundation</a></li>
<li>Cédric Champeau wrote a very interesting and insightful <a href="http://melix.github.io/blog/2015/02/who-is-groovy.html">analysis on who contributed to Groovy</a> over the years</li>
<li>John K. Waters from ADTMag on <a href="http://adtmag.com/articles/2015/03/04/groovy-joins-apache-foundation.aspx">Groovy to join Apache Software Foundation</a> following up an interview with Guillaume Laforge</li>
<li>Jochen Theodorou brainstorms on the path <a href="http://blackdragsview.blogspot.fr/2015/03/thoughts-about-new-meta-class-system.html">towards a revamped MOP for Groovy</a></li>
<li>MrHaki&rsquo;s Awesome Asciidoctor: <a href="http://mrhaki.blogspot.fr/2015/03/awesome-asciidoctor-use-inline.html">Use inline extension DSL with Gradle</a></li>
<li>MrHaki&rsquo;s Gradle Goodness: <a href="http://mrhaki.blogspot.fr/2015/03/gradle-goodness-define-system.html">Define system properties in gradle.properties</a></li>
<li><a href="http://blog.jetbrains.com/idea/2015/03/new-features-for-gradle-coming-in-intellij-idea-14-1/">New features for Gradle Coming in IntelliJ IDEA 14.1</a> by Andrey Cheptsov</li>
<li><a href="http://grooscript.org/react_example.html">Groovy, GrooScript with React.JS</a></li>
<li>Marcin Zajączkowski on <a href="https://solidsoft.wordpress.com/2015/03/09/spock-1-0-with-groovy-2-4-configuration-comparison-in-maven-and-gradle/">Spock 1.0 with Groovy 2.4 configuration comparison in Maven and Gradle</a></li>
<li>Benoît Hédiard writes about how to <a href="https://medium.com/@benorama/how-to-publish-your-grails-3-plugin-to-bintray-c341b24f567d">publish your Grails 3 plugin to Bintray</a></li>
<li>The Ratpack team published more information on <a href="http://www.ratpack.io/manual/0.9.15/intro.html#about_this_documentation">how the Ratpack documentation is working</a> and how code samples are tested</li>
<li>Ken Kousen in &ldquo;<a href="https://kousenit.wordpress.com/2015/03/10/if-a-method-arg-is-a-closure-for-crying-out-loud-pass-it-a-closure/">If a method arg is a closure, for crying out loud pass it a closure</a>&rdquo;</li>
<li><a href="http://blog.jetbrains.com/idea/2015/03/faster-groovy-compilation-in-intellij-idea-14-1/">Faster Groovy Compilation in IntelliJ IDEA</a> 14.1 by Peter Gromov</li>
<li>Grails tip: <a href="http://blog.jdriven.com/2015/03/grails-tip-rendering-errors-of-multiple-beans/">Rendering errors of multiple beans</a> by Albert van Veen</li>
<li>Get Groovy with <a href="http://zeroturnaround.com/blog/get-groovy-with-jrebel-and-grails/">JRebel and Grails</a> by Adam Koblentz</li>
<li>Working with <a href="http://www.intelligrape.com/blog/working-with-sass-scripts-in-grails-2-3-x/">SASS scripts in Grails 2.3</a> by Mansi Arora</li>
<li><a href="http://www.objectpartners.com/2015/03/03/customize-your-grails-test-reports/">Customize your Grails test reports</a> by Igor Shults</li>
<li><a href="http://www.objectpartners.com/2015/03/06/another-method-for-admin-screens-with-grails/">Another method for admin screens with Grails</a> by Mike Hostetler</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>The <a href="https://twitter.com/marc0der/status/573405378744098817">GVM project just reached 400 stars on Github</a>, keep star&rsquo;ing!</li>
<li>New <a href="http://docs.groovy-lang.org/next/html/documentation/core-object-orientation.html#_meta_annotations">documentation on Groovy meta-annotations</a> authored by Cédric Champeau</li>
<li>Kunal Dabir created a simple <a href="https://github.com/kdabir/directree">DSL to create directory trees and files</a></li>
<li>An updated <a href="http://grooscript.org/roadmap.html">roadmap for GrooScript</a>, including support for require.js</li>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="http://grydeske.net/news/show/86">Grails Diary</a> week 10 of 2015</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Thanks to Luke Daley and Will Erickson, <a href="https://twitter.com/CedricChampeau/status/573763143593295872">Dagger 2 will work out of the box with Groovy sources and Gradle 2.4</a></li>
<li>Cédric Champeau documented how you can <a href="https://twitter.com/CedricChampeau/status/573583638127845376">use closures as annotation parameters</a></li>
<li>Cédric Champeau is so much into AST transformations that he wrongly coined the <a href="https://twitter.com/CedricChampeau/status/573522774758662144">Apache Software Foundation with the AST acronym</a> instead of the ASF</li>
<li>Using <a href="https://twitter.com/davidthecoder/status/574223398945091584">Reactor with Groovy is a nice experience in data processing</a> says David Dawson</li>
<li>Dan Woods reminds us that <a href="https://twitter.com/danveloper/status/574298666623614976">Ratpack has a forum</a> in case you need help getting started</li>
<li>Tim Yates found some <a href="https://twitter.com/tim_yates/status/574270524139945984">Groovy biscuits</a> for the discerning Groovy developer</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Using <a href="https://gist.github.com/chiquitinxx/b01d0696cfabcc34527b">RxJs with GrooScript</a></li>
<li>Iván López authored a handy Groovy <a href="https://gist.github.com/lmivan/a752644419fb8d2f2221">script to generate the HTML of the Greach conference speakers pages</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>GR8Conf Europe 2015 announces a <a href="http://gr8conf.eu/#/">first batch of speakers</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Projects intends to join the Apache Software Foundation</title><link>https://glaforge.dev/posts/2015/03/04/groovy-projects-intends-to-join-the-apache-software-foundation/</link><pubDate>Wed, 04 Mar 2015 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2015/03/04/groovy-projects-intends-to-join-the-apache-software-foundation/</guid><description>&lt;p>The Groovy team is happy to announce its intention to join the Apache Software Foundation (ASF).&lt;/p>
&lt;p>Following up the recent announcement from Pivotal to end funding of full time developers for the Groovy programming language project, the team thought it would be appropriate to demonstrate to the Groovy community that the project is here for the long term, regardless of any funding from particular sponsoring organizations and regardless of any changes to the team of committers over time.&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy team is happy to announce its intention to join the Apache Software Foundation (ASF).</p>
<p>Following up the recent announcement from Pivotal to end funding of full time developers for the Groovy programming language project, the team thought it would be appropriate to demonstrate to the Groovy community that the project is here for the long term, regardless of any funding from particular sponsoring organizations and regardless of any changes to the team of committers over time.</p>
<p>We had several discussions both online and offline with representatives of various foundations, in particular with the Eclipse Foundation, the Software Conservancy foundation and the ASF.</p>
<p>We’ve been very grateful for the time and advice given by everybody, our users, committers to projects of those foundations, board members, etc. Those discussions have been very fruitful and enlightening. Those foundations are all very interesting and could be a great fit for the project, even if all have their pros and cons, of course. But overall, the Apache Software Foundation is the one that appears to be the best candidate considering our constraints and our philosophy.</p>
<p>We will soon start the process of submitting a proposal for incubation. If the proposal is accepted, the discussions we had on the mailing list highlighted some grey areas that we are going to deal with during that incubation process and we will be very happy to work with our mentors to make sure Groovy remains a major OSS project in the JVM ecosystem.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #60</title><link>https://glaforge.dev/posts/2015/03/03/groovy-weekly-60/</link><pubDate>Tue, 03 Mar 2015 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2015/03/03/groovy-weekly-60/</guid><description>&lt;p>This round number edition is pretty busy!&lt;/p>
&lt;p>Last week ended with a pretty sad note, with the news of Leonard Nimoy’s passing, the famous vulcan extraterrestrial from Star Trek. This certainly resonates with the Groovy community (and geeks &amp;amp; fans out large) because of our very own Spock testing framework. So let’s all gather and have a thought for this great actor and for his family.&lt;/p>
&lt;p>But as a tribute to this great actor and character, the long awaited news is that the &lt;a href="http://spockframework.github.io/spock/docs/1.0/release_notes.html">Spock testing framework releases its 1.0 version&lt;/a>!&lt;/p></description><content:encoded>
<![CDATA[<p>This round number edition is pretty busy!</p>
<p>Last week ended with a pretty sad note, with the news of Leonard Nimoy’s passing, the famous vulcan extraterrestrial from Star Trek. This certainly resonates with the Groovy community (and geeks &amp; fans out large) because of our very own Spock testing framework. So let’s all gather and have a thought for this great actor and for his family.</p>
<p>But as a tribute to this great actor and character, the long awaited news is that the <a href="http://spockframework.github.io/spock/docs/1.0/release_notes.html">Spock testing framework releases its 1.0 version</a>!</p>
<p>Still about releases, <a href="http://new.griffon-framework.org/news/griffon_2.2.0.html">Griffon 2.2</a> is out! As well as the last milestone of Grails 3 before it reaches release candidate status.</p>
<p>Following up the loss of funding for the Groovy and Grails projects, <a href="http://restlet.com/blog/2015/03/02/head-of-groovy-project-joins-restlet-to-lead-api-development-tools/">Guillaume Laforge, lead of Groovy, joins Restlet, a startup dedicated to API software</a>.</p>
<p>Another important news for the Groovy community is the <a href="http://www.codehaus.org/">closing of Codehaus</a>, which was the birth place of many projects from the ecosystem, including Groovy, Grails, Griffon, GPars, Geb, and others. All projects with some assets remaining there will have to migrate them before mid-May.</p>
<p>I’d like to congratulate Tim Yates for being so awesome and helpful on Stack Overflow, as he’s accounted that he replied to 14% of all questions tagged Groovy!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://spockframework.github.io/spock/docs/1.0/release_notes.html">Spock 1.0</a> released!</li>
<li><a href="http://new.griffon-framework.org/news/griffon_2.2.0.html">Griffon 2.2</a> released</li>
<li><a href="https://twitter.com/grailsframework/status/570976462439878656">Grails 3.0 milestone 2</a> released</li>
<li><a href="http://www.ratpack.io/versions/0.9.14">Ratpack 0.9.14</a> released</li>
<li><a href="http://groovy.329449.n5.nabble.com/ANN-Announcing-CodeNarc-0-23-td5722800.html">CodeNarc 0.23</a> released</li>
<li><a href="https://twitter.com/gvmtool/status/572008212712587264">GVM 2.2.1</a> released</li>
<li><a href="https://spring.io/blog/2015/02/27/spring-boot-1-1-11-released">Spring Boot 1.1.11</a> released</li>
<li><a href="https://spring.io/blog/2015/02/27/spring-boot-1-2-2-released">Spring Boot 1.2.2</a> released</li>
<li><a href="https://twitter.com/CedricChampeau/status/570995951835312128">JMH Gradle plugin 0.2.0</a> released4financeIT <a href="https://twitter.com/MGrzejszczak/status/570600241604509696">Gradle UpToDate plugin 1.2.0</a> release with proxy supportA Gradle plugin to <a href="http://android-arsenal.com/details/1/1609">upload Android APKs and app details to the Google Play Store</a></li>
<li><a href="https://twitter.com/AndreyHihlovski/status/572799840754782208">Gretty Gradle plugin 1.1.9</a> for running webapps on Tomcat &amp; Jetty from Gradle</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Head of Groovy project, <a href="http://restlet.com/blog/2015/03/02/head-of-groovy-project-joins-restlet-to-lead-api-development-tools/">Guillaume Laforge, joins Restlet</a> to lead API development tools</li>
<li>Guillaume Laforge&rsquo;s first post at his new gig about <a href="http://restlet.com/blog/2015/03/02/lets-make-apis-groovy-er/">making APIs groovy-er</a></li>
<li>The Register writes about &ldquo;<a href="http://www.theregister.co.uk/2015/03/02/groovy_java_guy_off_for_restlet/">Groovy Java guy off for Restlet</a>&rdquo;</li>
<li>Schalk Cronjé remarks that the <a href="https://twitter.com/ysb33r/status/572420138185715712">increasing number of Twitter congrats pouring out for Guillaume Laforge is a sign of the respect the Groovy community has for the man</a></li>
<li>Bertrand Delacretaz says that with half a million user, the project lead not fulltime anymore, <a href="https://twitter.com/bdelacretaz/status/572426911491756032">it&rsquo;s time that Groovy finds a neutral home</a></li>
<li>Graeme Rocher gives an <a href="http://grails.io/post/112130778738/grails-project-infrastructure-update">update on the Grails project infrastructure</a></li>
<li>A user guide was published explaining how to <a href="http://grails.github.io/grails-doc/3.0.x/guide/upgrading.html">migrate from Grails 2 to Grails 3</a></li>
<li>Kohsuke Kawaguchi suggests the Groovy team to <a href="http://kohsuke.org/2015/02/27/groovy-folks-time-to-start-agreeing/">start agreeing on a choice of a foundation</a></li>
<li>Danny Hyun describes how to use <a href="http://danhyun.github.io/asciidoctor-gradle-examples/">Gradle and Asciidoctor for HTML, PDF, ePUB, Deck.JS, Reveal.JS</a> outputs</li>
<li>How to <a href="http://planet.jboss.org/post/how_to_configure_geb_spock_with_gradle">configure Geb and Spock with Gradle</a></li>
<li><a href="http://www.eclecticlogic.com/2015/02/17/groovy-oddities/">Groovy oddities</a> by Karthik Abram</li>
<li>The Griffon team published the <a href="http://new.griffon-framework.org/releasenotes/griffon_2.2.0.html">Griffon 2.2 release notes</a></li>
<li>First <a href="http://new.griffon-framework.org/tutorials/">Griffon 2.2 tutorials</a> available</li>
<li>Marcin Grzejszczak on how he <a href="http://toomuchcoding.blogspot.com/2015/02/how-to-speed-up-your-gradle-build-from.html">reduced his Gradle build from 90 to 8 minutes</a></li>
<li>Craig Burke updates his <a href="http://www.craigburke.com/2015/03/02/groovy-document-builder-mpl-edition.html">Groovy Document Builder</a> with a friendly OSS license</li>
<li>Kyle Boon has up&rsquo;ed his Spock dependency to 1.0</li>
<li>After Spock 1.0, Iván López wonders <a href="https://twitter.com/ilopmar/status/572424545426116608">which framework will get to 1.0: Geb or Ratpack</a>?</li>
<li>Arthur Tsang blogs about the <a href="http://arthur-notes.youramaryllis.com/2015/02/moving-from-ratpack-0912-to-0913.html">changes required for moving from Ratpack 0.9.12 to 0.9.13</a> with the new configuration changes</li>
<li><a href="https://twitter.com/GebFramework/status/572689714760835072">Geb is now using Spock 1.0</a> just released</li>
<li>Dan Woods announces slick <a href="https://twitter.com/danveloper/status/572764621007814656">Groovy extensions for RxJava and &lsquo;ReactiveStreams in Ratpack&rsquo;s next version</a></li>
</ul>
<h2 id="interviews">Interviews</h2>
<ul>
<li>Victor Grazi of <a href="http://www.infoq.com/news/2015/03/Groovys-Laforge-Joins-Restlet">InfoQ interviewed Guillaume Laforge</a> on the fact he&rsquo;s joining the Restlet startup</li>
<li>Voxxed <a href="https://www.voxxed.com/blog/2015/03/with-foundation-decision-imminent-groovy-creator-moves-to-restlet/">interviews Guillaume Laforge with the imminent move of Groovy to a foundation and with joining Restlet</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Cédric Champeau presented <a href="https://twitter.com/CedricChampeau/status/572428885708697601">Groovy and Android, a winning pair</a>, at MCE 2015</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li><a href="http://www.codehaus.org/">Codehaus is shutting its doors</a></li>
<li>Andy Clément announces that <a href="https://twitter.com/andy_clement/status/570989272406233089">Groovy Eclipse 2.9.2 builds for Eclipse 4.4 and includes Groovy 2.3.10 and 2.4.1</a></li>
<li>Søren Berg Glasius and Guillaume Laforge <a href="https://twitter.com/glaforge/status/571044985463803904">collaborated on the GR8Conf Europe 2015 agenda</a></li>
<li><a href="http://groovy.329449.n5.nabble.com/GroovyLab-on-Raspberry-Pi-2-td5722839.html">GroovyLab now works on Raspberry Pi 2</a> by Stergios Papadimitriou</li>
<li><a href="https://grails.org/plugin/react-asset-pipeline">React support in Grails Asset Pipeline</a> plugin</li>
<li>With Codehaus closing, <a href="https://twitter.com/GebFramework/status/572305358913331201">Geb has moved its mailing-lists to Google Groups</a></li>
<li><a href="http://www.ratpack.io/manual/0.9.15/api/ratpack/logging/MDCInterceptor.html">Ratpack 0.9.14 adds support for Slf4j&rsquo;s Mapped Diagnostic Context</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Congrats to <a href="https://twitter.com/tim_yates/status/571231496004108288">Tim Yates who posted 14% of all answers to Stack Overflow questions on Groovy</a></li>
<li>The <a href="https://twitter.com/spockframework/status/572378381146300418">Spock Framework has its own twitter account</a></li>
<li>In memory of Leonard Nimoy&rsquo;s passing last week, Guillaume Laforge <a href="https://twitter.com/glaforge/status/571393595825111041">wishes the Spock framework to &ldquo;live long and prosper&rdquo;</a></li>
<li>Cédric Champeau outlines the fact that <a href="https://twitter.com/CedricChampeau/status/572412160304934912">Guillaume Laforge will continue to work on Groovy</a></li>
<li>With Codehaus closing, <a href="https://twitter.com/GebFramework/status/571234105313988609">Geb is going to move its mailing-list and issue tracker</a> soon</li>
<li>Marco Vermeulen continues working on <a href="https://twitter.com/marc0der/status/571765090342596609">GVM&rsquo;s Vendor API</a></li>
<li>Billy Yarosh <a href="https://twitter.com/keaplogik/status/570385013042225152">enjoys Groovy&rsquo;s documentation</a></li>
<li><a href="https://twitter.com/gvmtool/status/570979574768652288">Grails 3 milestone 2</a> available on GVM</li>
<li><a href="https://twitter.com/gvmtool/status/571217914944221185">Spring Boot 1.2.2</a> is available on GVM</li>
<li><a href="https://twitter.com/gvmtool/status/571218056095137792">Spring Boot 1.1.11</a> is available on GVM</li>
<li>Peter Niederwieser choses <a href="https://twitter.com/pniederw/status/572107004510126082">TravisCI for Spock&rsquo;s continuous integration</a> solution</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>The <a href="https://twitter.com/grooscript/status/571730552564420608">GrooScript project reached 150 followers on twitter</a>, let&rsquo;s add more followers!</li>
<li><a href="https://gist.github.com/chiquitinxx/a38ba0b54a405c11ece4">Mixing Angular.JS and GrooScript</a> using closures for controllers</li>
<li>Cédric Champeau created a <a href="https://twitter.com/CedricChampeau/status/571257801630220288">file / directory structure builder</a></li>
<li>Russell Hart shows how to <a href="https://github.com/ratpack/example-books/blob/latest/src/ratpack/ratpack.groovy#L44">externalize Ratpack&rsquo;s metrics collection configuration</a></li>
<li>Benoit Hediard on using <a href="https://gist.github.com/benorama/b26e7a94fe80e8c54daf">Grails on Amazon Beanstalk with TravisCI</a></li>
<li>A <a href="https://gist.github.com/chiquitinxx/e0c561547e0e1b690767">trait approach for GrooScript with Angular.JS</a> controllers</li>
</ul>
<h2 id="screencasts">Screencasts</h2>
<ul>
<li>A screencast introducing <a href="https://vimeo.com/zeroturnaround/review/120679908/75a58f2e13">JRebel&rsquo;s improved Grails support</a></li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>Dierk König notes that even the <a href="https://twitter.com/mittie/status/572497775872630784">small code listings in Groovy in Action 2nd edition makes for a 3MB archive</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>Most <a href="https://twitter.com/sbglasius/status/571045481666605056">speakers for GR8Conf Europe 2015</a> have been picked up</li>
<li><a href="https://twitter.com/alvaro_sanchez/status/571248810309095424">Álvaro Sánchez will be speaking at GR8Conf Europe</a> 2015</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #59</title><link>https://glaforge.dev/posts/2015/02/24/groovy-weekly-59/</link><pubDate>Tue, 24 Feb 2015 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2015/02/24/groovy-weekly-59/</guid><description>&lt;p>Groovy keeps rocking with a &lt;a href="https://glaforge.dev/posts/2015/02/18/joint-releases-of-groovy-2-4-1-and-2-3-10/">joint release of Groovy 2.4.1 and also a 2.3.10&lt;/a>!&lt;/p>
&lt;p>And did you notice the nice new design of the &lt;a href="https://grails.org/">new Grails website&lt;/a>, based on &lt;a href="http://www.groovy-lang.org/">Groovy’s website&lt;/a>? That’s a family look!&lt;/p>
&lt;p>Also, I’d like to end this short editorial with a big congrats to the &lt;a href="https://twitter.com/gvmtool/status/570138706973224960">GVM team who’s reached the 100,000 unique installs&lt;/a>! That’s a big milestone!&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>Joint releases of &lt;a href="https://glaforge.dev/posts/2015/02/18/joint-releases-of-groovy-2-4-1-and-2-3-10/">Groovy 2.4.1 and Groovy 2.3.10&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://groovy.329449.n5.nabble.com/GMavenPlus-1-4-Released-td5722753.html">GMavenPlus 1.4&lt;/a> released&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>Voxxed covers the &lt;a href="https://www.voxxed.com/blog/2015/02/groovy-latest-double-release-drop-foundation-deliberations/">latest double Groovy release drops and the deliberations on the choice of a foundation&lt;/a> for the project&lt;/li>
&lt;li>Shane Curcuru reflected on the fact of &lt;a href="https://medium.com/@shanecurcuru/its-groovy-to-join-a-foundation-4145732b4ec1">Groovy joining a foundation&lt;/a>&lt;/li>
&lt;li>MrHaki&amp;rsquo;s Groovy Goodness: &lt;a href="http://mrhaki.blogspot.fr/2015/02/groovy-goodness-access-xml-rpc-api.html">Access XML-RPC API&lt;/a>&lt;/li>
&lt;li>Kyle Boon on &lt;a href="http://kyleboon.org/blog/2015/02/23/groovy-gvm-and-go-gvm/">using Groovy and Go&amp;rsquo;s GVMs together&lt;/a>&lt;/li>
&lt;li>Installing &lt;a href="http://www.intelligrape.com/blog/sonarqube-all-in-one-code-quality-manager/">SonarQube for managing the quality of your Grails&lt;/a> project&lt;/li>
&lt;li>Miguel de la Cruz teaches a &lt;a href="http://www.kaleidos.net/blog/993/geb-101-simple-scripts/">Geb 101 course&lt;/a> in this article&lt;/li>
&lt;li>Laurent Napias wrote an article in French on &lt;a href="http://www.laurent-napias.com/post/2015/02/22/plus-de-groovy-avec-android-et-un-peu-de-swissknife-dedans">Groovy on Android with the SwissKnife&lt;/a> library&lt;/li>
&lt;li>Andy Wilkinson wrote about &lt;a href="https://spring.io/blog/2015/02/23/better-dependency-management-for-gradle">better dependency management for Gradle&lt;/a>&lt;/li>
&lt;li>Dustin Marx on &lt;a href="http://java.dzone.com/articles/writing-groovys-groovyutilnode">writing Groovy&amp;rsquo;s XmlParser&amp;rsquo;s Node content as XML&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.intelligrape.com/blog/integrating-pubnub-with-grails/">Integrating PubNub with Grails&lt;/a> by Sumant Thakur&lt;/li>
&lt;li>Ted Vinke on starting a &lt;a href="https://tedvinke.wordpress.com/2015/02/19/starting-a-video-blog-grails-in-the-first8friday-series/">video blog on Grails&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="news">News&lt;/h2>
&lt;ul>
&lt;li>Guillaume Laforge submitted &lt;a href="https://twitter.com/glaforge/status/567675689575669760">Groovy to the Google Summer of Code&lt;/a> program&lt;/li>
&lt;li>&lt;a href="https://kousenit.wordpress.com/2015/02/22/now-co-hosting-the-groovy-podcast/">Ken Kousen is co-hosting the Groovy podcast&lt;/a> with Peter Ledbrook&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/grailsframework/status/568471340686180352">Grails team launches its new website&lt;/a>, based on Groovy’s new site design&lt;/li>
&lt;li>Jacob Aae Mikkelsen&amp;rsquo;s &lt;a href="http://grydeske.net/news/show/84">Grails Diary&lt;/a> week 8 of 2015&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/570138706973224960">GVM crossed the 100k unique installs&lt;/a> mark!&lt;/li>
&lt;li>Cédric Champeau is happy having &lt;a href="https://twitter.com/CedricChampeau/status/568009999122894848">released two versions of Groovy in only two hours&lt;/a>&lt;/li>
&lt;li>Dean Macaulay finds that &lt;a href="https://twitter.com/dean_macaulay/status/567622801943564289">mixing Grails and Intellij IDEA makes a world of productivity&lt;/a>&lt;/li>
&lt;li>Tim Yates &lt;a href="https://twitter.com/tim_yates/status/567764313252896768">Groovy-logic library is listed on the MiniKanren website&lt;/a>&lt;/li>
&lt;li>Russel Winder wonders &lt;a href="https://twitter.com/russel_winder/status/568008750340173824">how Groovy release can be &amp;ldquo;hot&amp;rdquo; as Groovy is &amp;ldquo;cool&amp;rdquo;&lt;/a>!&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/568008971828768768">Groovy 2.3.10&lt;/a> is available on GVM&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/568009006440194048">Groovy 2.4.1&lt;/a> is available on GVM&lt;/li>
&lt;li>&lt;a href="https://twitter.com/grooscript/status/568529775150510080">GrooScript encourages speakers to use its vintage logo&lt;/a>&lt;/li>
&lt;li>Russel Winder is &lt;a href="https://twitter.com/russel_winder/status/568139020879974402">looking for some CSS guru for redesigning the GPars website&lt;/a>&lt;/li>
&lt;li>Dierk König notes that &lt;a href="https://twitter.com/mittie/status/568709714105765890">Groovy is 4 times in JavaOne 2014&amp;rsquo;s rock stars&lt;/a>&lt;/li>
&lt;li>Cédric Champeau notes the &lt;a href="https://twitter.com/CedricChampeau/status/568769788102619136">progress on the new Groovy documentation but highlights lots of sections looking for help for completion&lt;/a>&lt;/li>
&lt;li>Cédric Champeau highlights this &lt;a href="https://twitter.com/CedricChampeau/status/568767762417364993">commit of the Groovy executable documentation on type checking extensions&lt;/a>&lt;/li>
&lt;li>Answering Peter Ledbrook and Ken Kousen on the Groovy podcast, Cédric mentions that &lt;a href="https://twitter.com/CedricChampeau/status/569189775707652097">Grails&amp;rsquo; new site is a static site on Github, forked from the Groovy website&lt;/a>&lt;/li>
&lt;li>Kostis Kapelonis teases about &lt;a href="https://twitter.com/codepipes/status/569067464853749760">Groovy giving a 75% reduction of code lines against Java&lt;/a>, as an excerpt of his Spock book&lt;/li>
&lt;li>Kostis Kapelonis thinks the &lt;a href="https://twitter.com/codepipes/status/569775343806091264">&amp;ldquo;Making Java Groovy&amp;rdquo; book from Ken Kousen is one of the best Groovy for Java developers book&lt;/a> he&amp;rsquo;s ever read&lt;/li>
&lt;li>Versions 1.4.3 and 1.4.4 of the &lt;a href="https://twitter.com/pickypg/status/569885478775861248">ElasticSearch Groovy client released with special support for Grails 2.x&lt;/a>&lt;/li>
&lt;li>Schalk Cronjé is astonished that &lt;a href="https://twitter.com/ysb33r/status/570168038848405504">some Java developers still don&amp;rsquo;t know about Spock, Gradle, Bintray and Artifactory&lt;/a>&lt;/li>
&lt;li>Erik Pragt is working on an &lt;a href="https://twitter.com/epragt/status/570014405515186176">IntelliJ IDEA plugin in Groovy to support Ionic framework&lt;/a>&lt;/li>
&lt;li>Dan Woods and Iván López discuss about &lt;a href="https://twitter.com/ilopmar/status/570113950391664641">LogBack&amp;rsquo;s Groovy DSL configuration&lt;/a> and the tool to convert XML configurations into Groovy&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>Andrés Almiray shares his configuration for &lt;a href="https://gist.github.com/aalmiray/7369b977a68baca32e13">combining Asciidoctor, Ditaa based diagrams, PDF rendering, in your Gradle build&lt;/a>&lt;/li>
&lt;li>Vladimír Oraný is trying to &lt;a href="https://gist.github.com/musketyr/164bf82d4a07b3355a08">achieve better test isolation in Grails&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/grooscript/status/568475830072041472">Groovy making 3D figures dance in the browser thanks to GrooScript&lt;/a> and Three.js&lt;/li>
&lt;li>Danny Hun shares how to use &lt;a href="https://github.com/danhyun/asciidoctor-gradle-examples/tree/master/asciidoc-to-revealjs-example">Asciidoctor, Gradle and Reveal.JS&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="podcasts">Podcasts&lt;/h2>
&lt;ul>
&lt;li>Peter Ledbrook and Ken Kousen recorded &lt;a href="https://twitter.com/pledbrook/status/568896651055771648">Groovy podcast episode 7&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="books">Books&lt;/h2>
&lt;ul>
&lt;li>Konstantinos Kapelonis is authoring &lt;a href="https://twitter.com/mittie/status/567803572570828801">Java testing with Spock for Manning&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>The &lt;a href="https://twitter.com/springcentral/status/568948321047293953">SpringOne2GX 2015 call for paper is open&lt;/a>&lt;/li>
&lt;li>You can &lt;a href="http://gradlesummit.com/conference/santa_clara/2015/06/home">submit a talk to the Gradle Summit&lt;/a>&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/greachconf/status/568699197307920384">Greach conference has announced its final agenda&lt;/a>&lt;/li>
&lt;li>Andrew Reitz will be speaking about Groovy on Android at GR8Conf US&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>Groovy keeps rocking with a <a href="https://glaforge.dev/posts/2015/02/18/joint-releases-of-groovy-2-4-1-and-2-3-10/">joint release of Groovy 2.4.1 and also a 2.3.10</a>!</p>
<p>And did you notice the nice new design of the <a href="https://grails.org/">new Grails website</a>, based on <a href="http://www.groovy-lang.org/">Groovy’s website</a>? That’s a family look!</p>
<p>Also, I’d like to end this short editorial with a big congrats to the <a href="https://twitter.com/gvmtool/status/570138706973224960">GVM team who’s reached the 100,000 unique installs</a>! That’s a big milestone!</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Joint releases of <a href="https://glaforge.dev/posts/2015/02/18/joint-releases-of-groovy-2-4-1-and-2-3-10/">Groovy 2.4.1 and Groovy 2.3.10</a></li>
<li><a href="http://groovy.329449.n5.nabble.com/GMavenPlus-1-4-Released-td5722753.html">GMavenPlus 1.4</a> released</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Voxxed covers the <a href="https://www.voxxed.com/blog/2015/02/groovy-latest-double-release-drop-foundation-deliberations/">latest double Groovy release drops and the deliberations on the choice of a foundation</a> for the project</li>
<li>Shane Curcuru reflected on the fact of <a href="https://medium.com/@shanecurcuru/its-groovy-to-join-a-foundation-4145732b4ec1">Groovy joining a foundation</a></li>
<li>MrHaki&rsquo;s Groovy Goodness: <a href="http://mrhaki.blogspot.fr/2015/02/groovy-goodness-access-xml-rpc-api.html">Access XML-RPC API</a></li>
<li>Kyle Boon on <a href="http://kyleboon.org/blog/2015/02/23/groovy-gvm-and-go-gvm/">using Groovy and Go&rsquo;s GVMs together</a></li>
<li>Installing <a href="http://www.intelligrape.com/blog/sonarqube-all-in-one-code-quality-manager/">SonarQube for managing the quality of your Grails</a> project</li>
<li>Miguel de la Cruz teaches a <a href="http://www.kaleidos.net/blog/993/geb-101-simple-scripts/">Geb 101 course</a> in this article</li>
<li>Laurent Napias wrote an article in French on <a href="http://www.laurent-napias.com/post/2015/02/22/plus-de-groovy-avec-android-et-un-peu-de-swissknife-dedans">Groovy on Android with the SwissKnife</a> library</li>
<li>Andy Wilkinson wrote about <a href="https://spring.io/blog/2015/02/23/better-dependency-management-for-gradle">better dependency management for Gradle</a></li>
<li>Dustin Marx on <a href="http://java.dzone.com/articles/writing-groovys-groovyutilnode">writing Groovy&rsquo;s XmlParser&rsquo;s Node content as XML</a></li>
<li><a href="http://www.intelligrape.com/blog/integrating-pubnub-with-grails/">Integrating PubNub with Grails</a> by Sumant Thakur</li>
<li>Ted Vinke on starting a <a href="https://tedvinke.wordpress.com/2015/02/19/starting-a-video-blog-grails-in-the-first8friday-series/">video blog on Grails</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Guillaume Laforge submitted <a href="https://twitter.com/glaforge/status/567675689575669760">Groovy to the Google Summer of Code</a> program</li>
<li><a href="https://kousenit.wordpress.com/2015/02/22/now-co-hosting-the-groovy-podcast/">Ken Kousen is co-hosting the Groovy podcast</a> with Peter Ledbrook</li>
<li>The <a href="https://twitter.com/grailsframework/status/568471340686180352">Grails team launches its new website</a>, based on Groovy’s new site design</li>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="http://grydeske.net/news/show/84">Grails Diary</a> week 8 of 2015</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li><a href="https://twitter.com/gvmtool/status/570138706973224960">GVM crossed the 100k unique installs</a> mark!</li>
<li>Cédric Champeau is happy having <a href="https://twitter.com/CedricChampeau/status/568009999122894848">released two versions of Groovy in only two hours</a></li>
<li>Dean Macaulay finds that <a href="https://twitter.com/dean_macaulay/status/567622801943564289">mixing Grails and Intellij IDEA makes a world of productivity</a></li>
<li>Tim Yates <a href="https://twitter.com/tim_yates/status/567764313252896768">Groovy-logic library is listed on the MiniKanren website</a></li>
<li>Russel Winder wonders <a href="https://twitter.com/russel_winder/status/568008750340173824">how Groovy release can be &ldquo;hot&rdquo; as Groovy is &ldquo;cool&rdquo;</a>!</li>
<li><a href="https://twitter.com/gvmtool/status/568008971828768768">Groovy 2.3.10</a> is available on GVM</li>
<li><a href="https://twitter.com/gvmtool/status/568009006440194048">Groovy 2.4.1</a> is available on GVM</li>
<li><a href="https://twitter.com/grooscript/status/568529775150510080">GrooScript encourages speakers to use its vintage logo</a></li>
<li>Russel Winder is <a href="https://twitter.com/russel_winder/status/568139020879974402">looking for some CSS guru for redesigning the GPars website</a></li>
<li>Dierk König notes that <a href="https://twitter.com/mittie/status/568709714105765890">Groovy is 4 times in JavaOne 2014&rsquo;s rock stars</a></li>
<li>Cédric Champeau notes the <a href="https://twitter.com/CedricChampeau/status/568769788102619136">progress on the new Groovy documentation but highlights lots of sections looking for help for completion</a></li>
<li>Cédric Champeau highlights this <a href="https://twitter.com/CedricChampeau/status/568767762417364993">commit of the Groovy executable documentation on type checking extensions</a></li>
<li>Answering Peter Ledbrook and Ken Kousen on the Groovy podcast, Cédric mentions that <a href="https://twitter.com/CedricChampeau/status/569189775707652097">Grails&rsquo; new site is a static site on Github, forked from the Groovy website</a></li>
<li>Kostis Kapelonis teases about <a href="https://twitter.com/codepipes/status/569067464853749760">Groovy giving a 75% reduction of code lines against Java</a>, as an excerpt of his Spock book</li>
<li>Kostis Kapelonis thinks the <a href="https://twitter.com/codepipes/status/569775343806091264">&ldquo;Making Java Groovy&rdquo; book from Ken Kousen is one of the best Groovy for Java developers book</a> he&rsquo;s ever read</li>
<li>Versions 1.4.3 and 1.4.4 of the <a href="https://twitter.com/pickypg/status/569885478775861248">ElasticSearch Groovy client released with special support for Grails 2.x</a></li>
<li>Schalk Cronjé is astonished that <a href="https://twitter.com/ysb33r/status/570168038848405504">some Java developers still don&rsquo;t know about Spock, Gradle, Bintray and Artifactory</a></li>
<li>Erik Pragt is working on an <a href="https://twitter.com/epragt/status/570014405515186176">IntelliJ IDEA plugin in Groovy to support Ionic framework</a></li>
<li>Dan Woods and Iván López discuss about <a href="https://twitter.com/ilopmar/status/570113950391664641">LogBack&rsquo;s Groovy DSL configuration</a> and the tool to convert XML configurations into Groovy</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Andrés Almiray shares his configuration for <a href="https://gist.github.com/aalmiray/7369b977a68baca32e13">combining Asciidoctor, Ditaa based diagrams, PDF rendering, in your Gradle build</a></li>
<li>Vladimír Oraný is trying to <a href="https://gist.github.com/musketyr/164bf82d4a07b3355a08">achieve better test isolation in Grails</a></li>
<li><a href="https://twitter.com/grooscript/status/568475830072041472">Groovy making 3D figures dance in the browser thanks to GrooScript</a> and Three.js</li>
<li>Danny Hun shares how to use <a href="https://github.com/danhyun/asciidoctor-gradle-examples/tree/master/asciidoc-to-revealjs-example">Asciidoctor, Gradle and Reveal.JS</a></li>
</ul>
<h2 id="podcasts">Podcasts</h2>
<ul>
<li>Peter Ledbrook and Ken Kousen recorded <a href="https://twitter.com/pledbrook/status/568896651055771648">Groovy podcast episode 7</a></li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>Konstantinos Kapelonis is authoring <a href="https://twitter.com/mittie/status/567803572570828801">Java testing with Spock for Manning</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>The <a href="https://twitter.com/springcentral/status/568948321047293953">SpringOne2GX 2015 call for paper is open</a></li>
<li>You can <a href="http://gradlesummit.com/conference/santa_clara/2015/06/home">submit a talk to the Gradle Summit</a></li>
<li>The <a href="https://twitter.com/greachconf/status/568699197307920384">Greach conference has announced its final agenda</a></li>
<li>Andrew Reitz will be speaking about Groovy on Android at GR8Conf US</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Joint releases of Groovy 2.4.1 and 2.3.10</title><link>https://glaforge.dev/posts/2015/02/18/joint-releases-of-groovy-2-4-1-and-2-3-10/</link><pubDate>Wed, 18 Feb 2015 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2015/02/18/joint-releases-of-groovy-2-4-1-and-2-3-10/</guid><description>&lt;p>The Groovy development team is happy to announce the joint releases of the Groovy 2.4.1 and Groovy 2.3.10 of the Groovy programming language for the Java platform.&lt;/p>
&lt;p>Both releases are bug fix releases, and while Groovy 2.4.1 is the latest official stable branch, we thought it might be helpful to some projects who are still on the 2.3.x line to get a final release for that branch. But going forward, the 2.3.x branch won’t see any upcoming release.&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy development team is happy to announce the joint releases of the Groovy 2.4.1 and Groovy 2.3.10 of the Groovy programming language for the Java platform.</p>
<p>Both releases are bug fix releases, and while Groovy 2.4.1 is the latest official stable branch, we thought it might be helpful to some projects who are still on the 2.3.x line to get a final release for that branch. But going forward, the 2.3.x branch won’t see any upcoming release.</p>
<p>You can learn more about all the tickets closed by reading:</p>
<ul>
<li>
<p>the <a href="http://groovy-lang.org/changelogs/changelog-2.4.1.html">Groovy 2.4.1 changelog</a></p>
</li>
<li>
<p>the <a href="http://groovy-lang.org/changelogs/changelog-2.3.10.html">Groovy 2.3.10 changelog</a></p>
</li>
</ul>
<p>For reference, you can also read the <a href="http://groovy-lang.org/releasenotes/groovy-2.4.html">release notes of Groovy 2.4</a> if you haven&rsquo;t had a chance to since the announcement.</p>
<p>Head over to the <a href="http://www.groovy-lang.org/download.html">download section of the new Groovy website</a> to download the binary distribution, or update your dependencies accordingly with those new version numbers.</p>
<p>Thanks a lot for all those who contributed to this release!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #58</title><link>https://glaforge.dev/posts/2015/02/17/groovy-weekly-58/</link><pubDate>Tue, 17 Feb 2015 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2015/02/17/groovy-weekly-58/</guid><description>&lt;p>The past week’s been pretty calm, in terms of news, but a few of them really stood out.&lt;/p>
&lt;p>&lt;a href="http://www.forbes.com/sites/danwoods/2015/02/11/how-a-netflix-tech-innovation-can-unleash-creativity-in-your-business/print/">Netflix again leverages Groovy for scripting its user experience API&lt;/a> (additional coverage following last week’s announcement).&lt;/p>
&lt;p>The Gradleware team delivered &lt;a href="http://forums.gradle.org/gradle/topics/gradle-2-3-released">Gradle 2.3&lt;/a>! Congrats guys!&lt;/p>
&lt;p>And closer to home, a very important discussion about &lt;a href="http://groovy.329449.n5.nabble.com/Moving-Groovy-to-a-Foundation-td5722483.html">moving Groovy into a foundation&lt;/a> has started, inviting members and representatives of different foundations to join and offer their views and experience on the matter.&lt;/p></description><content:encoded>
<![CDATA[<p>The past week’s been pretty calm, in terms of news, but a few of them really stood out.</p>
<p><a href="http://www.forbes.com/sites/danwoods/2015/02/11/how-a-netflix-tech-innovation-can-unleash-creativity-in-your-business/print/">Netflix again leverages Groovy for scripting its user experience API</a> (additional coverage following last week’s announcement).</p>
<p>The Gradleware team delivered <a href="http://forums.gradle.org/gradle/topics/gradle-2-3-released">Gradle 2.3</a>! Congrats guys!</p>
<p>And closer to home, a very important discussion about <a href="http://groovy.329449.n5.nabble.com/Moving-Groovy-to-a-Foundation-td5722483.html">moving Groovy into a foundation</a> has started, inviting members and representatives of different foundations to join and offer their views and experience on the matter.</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://forums.gradle.org/gradle/topics/gradle-2-3-released">Gradle 2.3</a> released</li>
<li><a href="http://forums.gradle.org/gradle/topics/gradle-2-3-rc-3-is-now-available-for-testing">Gradle 2.3-rc-3</a> is ready for testing</li>
<li><a href="https://groups.google.com/forum/#!topic/spockframework/008xHyM5DEI">Spock-reports 1.2.4</a> released</li>
<li><a href="https://www.linkedin.com/groupItem?view=&amp;item=5972153940031131649&amp;type=member&amp;gid=76751&amp;trk=eml-b2_anet_digest_weekly-hero-12-grouppost-0&amp;midToken=AQES0rgmUQCbHg&amp;fromEmail=fromEmail&amp;ut=3j0nkf-NtM9CE1">gServ 0.9.6</a> released, a Groovy-based container-less REST framework</li>
<li><a href="https://twitter.com/nagai_masato/status/566531438795698180">GBench 0.4.3</a> released</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>A Forbes article explains how a <a href="http://www.forbes.com/sites/danwoods/2015/02/11/how-a-netflix-tech-innovation-can-unleash-creativity-in-your-business/print/">Netflix innovation unleash the creativity in their business, thanks to Nicobar, their Groovy based scripting solution</a></li>
<li>Jaxenter on the ongoing discussion about <a href="http://jaxenter.com/will-groovy-soon-eclipse-apache-project-114536.html">moving the Groovy project to a foundation</a></li>
<li>Masato Nagai <a href="http://nagaimasato.blogspot.fr/2015/02/gbench-043-released-lets-benchmark-as.html?spref=tw">benchmarks Groovy&rsquo;s as operator</a> between Groovy 2.3 and 2.4, using GBench</li>
<li>Jeff Beck writes about <a href="http://beckje01.com/blog/2015/02/14/grails-integration-testing-of-complex-transactions/">Grails integration testing of complex transactions</a></li>
<li><a href="http://hosain.net/2015/02/07/getting-started-with-android-development-using-groovy-2.4-and-android-studio.html">Getting started with Android development using Groovy 2.4</a> and Android Studio</li>
<li><a href="http://www.jroller.com/ie/entry/releasing_a_project_to_maven">Releasing a project to Maven Central with Gradle</a> by John McClean</li>
<li><a href="http://davesmith.me//blog/2015/geb-gradle-rules.html">Geb and Gradle rules</a> by Dave Smith</li>
<li><a href="http://davesmith.me//blog/2015/gradle-devops-rules.html">Gradle rules</a> by Dave Smith</li>
</ul>
<h2 id="mailing-lists">Mailing-lists</h2>
<ul>
<li>An important discussion on <a href="http://groovy.329449.n5.nabble.com/Moving-Groovy-to-a-Foundation-td5722483.html">moving Groovy to a foundation</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li><a href="http://steveonjava.com/congratulations-to-the-2014-javaone-rock-stars/">Cédric Champeau becomes a JavaOne rock star</a> thanks to his excellent presentation on rethinking APIs with traits during JavaOne 2014</li>
<li>Duncan Dickinson launches his <a href="http://www.groovy-tutorial.org/">Groovy 2 tutorial series</a>, with 4 tutorials on getting started, variables, operators, and control flow statements</li>
<li>You can vote for an <a href="https://youtrack.jetbrains.com/issue/IDEA-136587">improvement to let Gradle-built IntelliJ IDEA projects support clickable source code files</a> from the output window</li>
<li><a href="http://grydeske.net/news/show/83">Grails Diary</a> Week 7 of 2015 by Jacob Aae Mikkelsen</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Cédric Champeau calls for feedback and <a href="https://twitter.com/CedricChampeau/status/565548766946926592">seeks advice on the idea of moving Groovy to a foundation</a></li>
<li>Andrés Almiray publishes the first Griffon tutorial for the next iteration of the Griffon website</li>
<li>The <a href="https://twitter.com/marc0der/status/566758590472794112">GVM vendor API</a> is now in a dev environment</li>
<li>GrooScript updated its <a href="https://twitter.com/grooscript/status/565282808307150848">groovy ecosystem frameworks</a> page</li>
<li>The JFrog crew earned a <a href="https://twitter.com/jfrog/status/565589535862841346">JavaOne Rock Star title thanks to their Groovy puzzlers talk</a></li>
<li><a href="https://twitter.com/gvmtool/status/567274822141763584">Gradle 2.3</a> available through GVM</li>
<li>The Groovy puzzlers guys are <a href="https://twitter.com/groovypuzzlers/status/567316156411691009">looking for new Groovy puzzlers for their second season</a>!</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Tim Yates and Cédric Champeau are playing <a href="https://twitter.com/tim_yates/status/565490899963375616">Groovy code golf on Fibonacci</a></li>
<li>Tim Yates shares some <a href="https://gist.github.com/timyates/d97d613e4379434ff7a8">snippets combining Groovy and GroovyFX</a></li>
</ul>
<h2 id="podcasts">Podcasts</h2>
<ul>
<li>Episode 48 of the JavaPubHouse is about using <a href="https://twitter.com/fguime/status/566098122901041152">Groovy and Gradle for Java projects</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>Iván López will be speaking about being a <a href="https://twitter.com/ilopmar/status/565806105042120704">full-stack Groovy developer</a> at the Spring.IO conference</li>
<li>Marco Vermeulen notes that <a href="https://twitter.com/marc0der/status/566136563214876672">SpringOne2GX keeps its 2GX suffix and that the CFP is open</a></li>
<li>Craig Burke will be talking about <a href="https://twitter.com/craigburke1/status/566637455982731264">Angular.JS in a Groovy world</a> at GR8Conf US</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #57</title><link>https://glaforge.dev/posts/2015/02/10/groovy-weekly-57/</link><pubDate>Tue, 10 Feb 2015 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2015/02/10/groovy-weekly-57/</guid><description>&lt;p>So what’s Groovy this week? The Groovy community is still celebrating the 4.5+ million downloads of Groovy last year, is drive testing the upcoming Gradle 2.3, while Greach is announcing its impressive roster of speakers.&lt;/p>
&lt;p>But there’s an item of news dear to my heart this week, with the “go to production” message from Dierk about Groovy in Action, second edition! Many times the book has been revised to catch up with the latest and greatest of Groovy, and this new edition will bring you up-to-date with Groovy 2.4, so you can expect the official print and e-book final versions imminently!&lt;/p></description><content:encoded>
<![CDATA[<p>So what’s Groovy this week? The Groovy community is still celebrating the 4.5+ million downloads of Groovy last year, is drive testing the upcoming Gradle 2.3, while Greach is announcing its impressive roster of speakers.</p>
<p>But there’s an item of news dear to my heart this week, with the “go to production” message from Dierk about Groovy in Action, second edition! Many times the book has been revised to catch up with the latest and greatest of Groovy, and this new edition will bring you up-to-date with Groovy 2.4, so you can expect the official print and e-book final versions imminently!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://twitter.com/gradleware/status/562992965724172288">Gradle 2.3-rc-2</a> is available</li>
<li><a href="https://twitter.com/grailsflow/status/563301303464755201">GrailsFlow 1.5</a> released with Bootstrap 3</li>
<li>Joachim Baumann published the <a href="https://bintray.com/groovy/Distributions/Windows-Installer/groovy-2.4.0-installer/view">Windows installer for Groovy 2.4</a></li>
<li><a href="https://twitter.com/andrewreitz_/status/564908080593244160">Spock-Android 1.1</a> released</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li><a href="http://techblog.netflix.com/2015/02/nicobar-dynamic-scripting-library-for.html">Netflix open sources a new Groovy-based scripting library</a> for customizing their API UIs</li>
<li><a href="https://www.voxxed.com/blog/2015/02/speed-gradle-build-90-8-minutes/">Accelerate your Gradle build time</a> from 90 to 8 minutes</li>
<li>MrHaki&rsquo;s Spocklight: <a href="http://mrhaki.blogspot.fr/2015/02/spocklight-capture-and-assert-system.html">Capture and assert system output</a></li>
<li>Jochen Theodorou on <a href="http://blackdragsview.blogspot.fr/2015/02/getting-rid-of-compareto-for.html">getting rid of compareTo in favor of ==</a></li>
<li><a href="http://www.citytechinc.com/us/en/blog/2015/02/aem-groovy-console-new-features.html">New features for the venerable AEM</a> (Adobe CQ) Groovy Console</li>
<li><a href="http://blog.varunin.com/2015/01/developing-web-automation-framework.html">Developing a web automation framework using Geb</a> and Groovy by Varun Menon</li>
<li>The <a href="http://blog.jetbrains.com/idea/2015/02/intellij-idea-14-1-eap-is-available/">new EAP of IntelliJ IDEA features enhanced Gradle support</a> and allows you to use the Groovy-Eclipse compiler</li>
<li><a href="http://www.objectpartners.com/2015/02/03/groovy-closures-create-tidy-flexible-services/">Groovy Closures create tidy, flexible services</a> by Ben Brunk</li>
<li><a href="http://www.intelligrape.com/blog/significance-of-mappedby-in-grails-domain/">Significance of “mappedBy” in Grails domain classes</a> by Naman Gautam</li>
<li><a href="http://www.mscharhag.com/2015/02/creating-android-apps-with-groovy.html">Creating Android apps with Groovy 2.4</a> by Michael Scharhag</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li><a href="http://www.infoq.com/presentations/reactive-arch-grails">Reactive Oriented Architecture with Grails</a> by Steve Pember recorded at SpringOne2GX 2014</li>
<li>Cédric Champeau posted his slides from the MCE Conference in Poland about <a href="https://speakerdeck.com/melix/groovy-and-android-a-winning-pair-2">Groovy &amp; Android, a winning pair</a></li>
<li><a href="https://www.parleys.com/talk/groovy-light-java-8">Groovy in the light of Java 8</a> by Cédric Champeau at JavaOne 2014</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>Dierk König announces that <a href="https://twitter.com/mittie/status/563825962606149632">all the content of the second edition of Groovy in Action is committed to production</a></li>
<li>Dierk König tells that the <a href="https://twitter.com/mittie/status/564461905281294336">second edition of Groovy in Action contains 20 chapters and 9 appendices</a></li>
</ul>
<h2 id="podcasts">Podcasts</h2>
<ul>
<li>Peter Ledbrook posted <a href="https://twitter.com/pledbrook/status/563371370974035968">Episode 6 of the Groovy podcast</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>A <a href="https://github.com/jdereg/n-cube">Groovy based rules engine</a>, decision table &amp; tree, templating engine</li>
<li>A <a href="https://github.com/tkruse/gpi">Groovy package installer</a>, &ldquo;gpi&rdquo;, from Thibault Kruse</li>
<li>Jacob Aae Mikkelsen publishes <a href="http://grydeske.net/news/show/82">Grails Diary</a> week 6</li>
<li>The list of <a href="https://github.com/grails3-plugins/">Grails plugins ready for Grails 3</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Cédric Champeau summarizes the nice upward trend for <a href="https://twitter.com/CedricChampeau/status/562887140271726593">Groovy&rsquo;s yearly download numbers, reaching 4.5 millions in 2014</a></li>
<li>Craig Burke found a parallel with iOS&rsquo; &ldquo;there&rsquo;s an app for that&rdquo;, with &ldquo;<a href="https://twitter.com/craigburke1/status/562630876883083265">in Groovy, there&rsquo;s an AST transformation for that</a>&rdquo;</li>
<li>Craig Burke is working on a <a href="https://twitter.com/craigburke1/status/563413112238649345">PdfBox based document builder</a> with a friendly open source license</li>
<li>The <a href="https://twitter.com/gebframework/status/563270465452376064">book of Geb is being migrated to Asciidoctor</a> with automatically tested code snippets</li>
<li>The new <a href="https://twitter.com/gebframework/status/563041355816239104">Geb twitter account</a> reached hundred followers</li>
<li><a href="https://twitter.com/gvmtool/status/564691564665266177">Gradle 2.3-rc-3</a> available through GVM</li>
<li>You can <a href="https://twitter.com/nisafla/status/562303038912614400">fill a survey to help make Gradle tutorials better</a></li>
<li>Luke Daley announces a new <a href="https://twitter.com/ldaley/status/563113387169562624">Gradle plugin for running tests within Docker containers</a></li>
<li>Peter Ledbrook learned that <a href="https://twitter.com/pledbrook/status/563434449191579648">Groovy&rsquo;s type checker can infer the component type of list literals</a></li>
<li>Clint Liddick says <a href="https://twitter.com/ClintLiddick/status/563817158720118784">IntelliJ IDEA&rsquo;s Groovy console is way nicer than an hypothetic Java REPL</a></li>
<li>You can watch <a href="https://twitter.com/grooscript/status/565265212761464832">GrooScript in action</a> at the Greach conference</li>
<li>A last final MEAP of Groovy in Action second edition will be out soon, says Dierk König</li>
<li>Sean Gilligan works on a <a href="https://twitter.com/jsr354/status/564420791224508416">Groovy support for JSR-354 / Money</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="https://twitter.com/greachconf/status/563296040539811840">Greach tweets about its roster of speakers</a></li>
<li><a href="https://twitter.com/greachconf/status/563294745183539201">More speakers for the Greach</a> conference</li>
<li>The <a href="https://twitter.com/greachconf/status/565084022733160448">Greach conference agenda</a> is online</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #56</title><link>https://glaforge.dev/posts/2015/02/03/groovy-weekly-56/</link><pubDate>Tue, 03 Feb 2015 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2015/02/03/groovy-weekly-56/</guid><description>&lt;p>Congrats to the GrooScript team with the &lt;a href="http://grooscript.org/announcement.html">GrooScript 1.0&lt;/a> release! With GrooScript, if you will, you can have a full Groovy experience, from the build (Gradle), backend (Groovy, Grails, Ratpack), mobile (Groovy on Android), up to the front-end web layer with Groovy translated into JavaScript!&lt;/p>
&lt;p>Another impressive and widely acclaimed release is the &lt;a href="https://github.com/grails/grails-core/releases/tag/v3.0.0.M1">first milestone of Grails 3&lt;/a>! You can have a look at the &lt;a href="http://grails.github.io/grails-doc/3.0.x/guide/introduction.html#whatsNew">novelties in Grails 3&lt;/a>, and watch a &lt;a href="https://www.youtube.com/watch?v=aro3_RZqgtU">screencast&lt;/a> about it.&lt;/p></description><content:encoded>
<![CDATA[<p>Congrats to the GrooScript team with the <a href="http://grooscript.org/announcement.html">GrooScript 1.0</a> release! With GrooScript, if you will, you can have a full Groovy experience, from the build (Gradle), backend (Groovy, Grails, Ratpack), mobile (Groovy on Android), up to the front-end web layer with Groovy translated into JavaScript!</p>
<p>Another impressive and widely acclaimed release is the <a href="https://github.com/grails/grails-core/releases/tag/v3.0.0.M1">first milestone of Grails 3</a>! You can have a look at the <a href="http://grails.github.io/grails-doc/3.0.x/guide/introduction.html#whatsNew">novelties in Grails 3</a>, and watch a <a href="https://www.youtube.com/watch?v=aro3_RZqgtU">screencast</a> about it.</p>
<p>The Ratpack team has also been pretty busy with <a href="http://www.ratpack.io/versions/0.9.13">Ratpack 0.9.13</a>, with over 17k lines of code changed in this release!</p>
<p>Guillaume Laforge also announced that <a href="https://twitter.com/glaforge/status/562590782084112384">Groovy was downloaded over 4 million times from Maven Central</a>, without even counting the downloads of the binary distributions from Codehaus or Bintray. In total, Groovy will likely be beyond 5 million times in total, compared with 3 millions the past year.</p>
<h2 id="releases">Releases</h2>
<ul>
<li>The <a href="https://github.com/grails/grails-core/releases/tag/v3.0.0.M1">first milestone release of Grails 3</a> is out!</li>
<li><a href="http://grooscript.org/announcement.html">GrooScript 1.0</a> released, the Groovy to JavaScript transpiler</li>
<li><a href="http://www.ratpack.io/versions/0.9.13">Ratpack 0.9.13</a> released, a &ldquo;big one&rdquo;!</li>
<li><a href="https://twitter.com/davydotcom/status/560503261137231872">Grails asset-pipeline plugin v2.1.1</a></li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>InfoQ covers the <a href="http://www.infoq.com/news/2015/01/groovy14-android">Groovy 2.4 release and highlights the Android support</a></li>
<li>MrHaki&rsquo;s Groovy Goodness:
<ul>
<li><a href="http://mrhaki.blogspot.fr/2015/01/groovy-goodness-getting-all-but-last.html">getting all but the last element in a collection with the init method</a></li>
<li><a href="http://mrhaki.blogspot.fr/2015/01/groovy-goodness-getting-indices-of.html">getting the indices of a collection</a></li>
<li><a href="http://mrhaki.blogspot.fr/2015/01/groovy-goodness-pop-and-push-items-in.html">pop and push items in a list</a></li>
</ul>
</li>
<li>Mariano Gonzalez wrote about <a href="http://blogs.mulesoft.org/mule-3-6-polyglot-programming/">polyglot programming in Mule</a>, with the scripting pack now part of the distribution, and showing an example with Groovy</li>
<li><a href="http://davydotcom.com/blog/2015-01-29-grails-3-0-0-m1-asset-pipeline-tips-tricks">Grails 3.0.0.M1 Asset-Pipeline tips and tricks</a> by David Estes</li>
<li>Grails Clean Code: <a href="http://www.intelligrape.com/blog/grails-clean-code-configure-codenarc-plugin/">Configure Codenarc plugin</a> by Uday Pratap Singh</li>
<li>Building a <a href="https://www.accelebrate.com/blog/building-geolocation-web-application-groovy-grails/">geolocation web application with Groovy and Grails</a> by Ken Kousen</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>The <a href="http://www.infoq.com/presentations/paypal-api-evolution">PayPal API platform is using Groovy as its orchestration language</a>, as mentioned in this presentation given at QCon San Francisco 2014</li>
<li>SpringOne2GX 2014 Replay
<ul>
<li><a href="https://spring.io/blog/2015/01/26/springone2gx-2014-replay-groovy-for-system-administrators">Groovy for system administrators</a> by Dan Woods</li>
<li><a href="http://www.infoq.com/presentations/ratpack-2014">The Ratpack web framework</a> by Dan Woods</li>
<li><a href="http://www.infoq.com/presentations/clustering-terracota-quartz-grails">Web clustering, integration with Terracotta, BigMemory, Quartz &amp; Grails</a> by Ryan Vanderwerf</li>
<li><a href="http://www.infoq.com/presentations/grails-testing-2014">Testing Grails</a> by Ken Kousen</li>
<li><a href="http://www.infoq.com/presentations/cd-gradle-jenkins-2014">Building a Continuous Delivery pipeline with Gradle and Jenkins</a> by Peter Niederwieser</li>
</ul>
</li>
<li>Building a <a href="https://www.accelebrate.com/blog/building-geolocation-web-application-groovy-grails/">geolocation web application with Groovy and Grails</a> by Ken Kousen</li>
</ul>
<h2 id="screencasts">Screencasts</h2>
<ul>
<li>A <a href="https://twitter.com/grailsframework/status/560752646093832192">screencast preview of Grails 3</a> by Graeme Rocher for the M1 release</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="http://grydeske.net/news/show/81">Grails Diary</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Guillaume Laforge announced <a href="https://twitter.com/glaforge/status/562590782084112384">Groovy being downloaded over 4 million times in 2014 from Maven Central</a>. With the Codehaus distribution figures, it might mean beyond 5 million total downloads!</li>
<li>Cédric Champeau realizes <a href="https://twitter.com/cedricchampeau/status/560837091764887552">how huge Groovy is when one has to write its documentation</a>! Or when you notice that Groovy in Action (second edition) is over 1000 pages!</li>
<li><a href="https://twitter.com/mg6maciej/status/560865008830382081">Testing Android apps with Espresso is very expressive in Groovy</a> remarks Maciej Górski</li>
<li>Victor Trakhtenberg needs some <a href="https://twitter.com/victortr75/status/560393895658283009">votes for his Groovy talk</a> on confessions of a Java developer that fell in love with Groovy</li>
<li>Dierk König says that <a href="https://twitter.com/mittie/status/562570994918100993">Groovy in Action, second edition, contains over 600 code listings</a></li>
<li>John Engelman counted <a href="https://twitter.com/johnrengelman/status/562081630677127168">17k code line changes in Ratpack 0.9.13</a></li>
<li>At FOSDEM, the <a href="https://twitter.com/thetaph1/status/561891670082732032">Oracle team mentioned Groovy&rsquo;s contributions to debugging JDK 8</a>, thanks to Cédric Champeau&rsquo;s bug reports</li>
<li>You can get grooscript.js from from NPM and Bower</li>
<li>Marco Vermeulen is <a href="https://twitter.com/marc0der/status/561993267819323393">impressed with Grails 3</a> and how the Grails team outdid themselves!</li>
<li><a href="https://twitter.com/gvmtool/status/560920057916948480">Grails 3.0 milestone 1</a> is available on GVM</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Philippe Charrière implemented his first <a href="https://github.com/k33g/kiss-groovy/blob/master/app/main.groovy#L16">http server framework with Groovy</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="https://twitter.com/marc0der/status/562142093267959808">Marco Vermeulen will be speaking at Greach</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #55</title><link>https://glaforge.dev/posts/2015/01/27/groovy-weekly-55/</link><pubDate>Tue, 27 Jan 2015 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2015/01/27/groovy-weekly-55/</guid><description>&lt;p>This week is a bit more positive than the previous with the announcement of the &lt;a href="https://glaforge.dev/posts/2015/01/21/groovy-2-4-released/">release of Groovy 2.4&lt;/a>!&lt;/p>
&lt;p>But some interesting and thoughtful pieces have also been written about last week’s sad news about Pivotal divesting in Groovy and Grails.&lt;/p>
&lt;p>In spite of this, &lt;a href="https://twitter.com/springone2gx/status/559903903756861440">SpringOne2GX 2015 has been announced with 4 Groovy and Grails tracks&lt;/a>, in Washington DC, mid-September.&lt;/p>
&lt;p>Last but not least, the &lt;a href="https://twitter.com/mittie/status/558011051313491969">second edition of Groovy in Action is going into production&lt;/a>!&lt;/p></description><content:encoded>
<![CDATA[<p>This week is a bit more positive than the previous with the announcement of the <a href="https://glaforge.dev/posts/2015/01/21/groovy-2-4-released/">release of Groovy 2.4</a>!</p>
<p>But some interesting and thoughtful pieces have also been written about last week’s sad news about Pivotal divesting in Groovy and Grails.</p>
<p>In spite of this, <a href="https://twitter.com/springone2gx/status/559903903756861440">SpringOne2GX 2015 has been announced with 4 Groovy and Grails tracks</a>, in Washington DC, mid-September.</p>
<p>Last but not least, the <a href="https://twitter.com/mittie/status/558011051313491969">second edition of Groovy in Action is going into production</a>!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://glaforge.dev/posts/2015/01/21/groovy-2-4-released/">Groovy 2.4</a> released</li>
<li><a href="http://groovy.329449.n5.nabble.com/GMaven-1-3-Released-td5722239.html">GMaven Plus 1.3</a> released</li>
<li><a href="https://twitter.com/arasthel92/status/559659062309449728">SwissKnife 1.2.2</a> released</li>
<li><a href="https://twitter.com/grooscript/status/557999421691269120">Second release candidate for GrooScript 1.0</a></li>
<li><a href="https://twitter.com/grooscript/status/558000095070015488">GrooScript Gradle plugin 0.10</a> released</li>
<li><a href="https://twitter.com/gradleware/status/560099446508519426">Gradle 2.3-rc-1</a> available for testing</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Voxxed notes that <a href="https://www.voxxed.com/blog/2015/01/groovy-verse-back-business-2-4-release/">Groovy is back to business with its 2.4 release</a></li>
<li>Object Partners writes <a href="http://www.objectpartners.com/2015/01/23/keep-calm-groovy-on/">Keep Calm and Groovy on</a>!</li>
<li>Henrique Lobo Weissmann compares <a href="http://www.itexto.com.br/devkico/en/?p=69">Groovy and Grails to &ldquo;bridges&rdquo; technology</a></li>
<li>Dan Vega shares his <a href="http://therealdanvega.com/blog/2015/01/20/pivotal-drops-groovy-grails-sponsorship">thoughtful views on the Groovy &amp; Grails loss of funding</a> from Pivotal</li>
<li>Peter Ledbrook shares his <a href="http://blog.cacoethes.co.uk/groovyandgrails/the-future-of-groovy">thoughts on the future of Groovy</a></li>
<li>TheRegister covers the <a href="http://www.theregister.co.uk/2015/01/22/open_source_java_grails_and_groovy_as_pivotal_pulls_out/">loss of sponsorship from Pivotal of the Groovy and Grails projects</a></li>
<li>Dustin Marx writes about the <a href="http://java.dzone.com/articles/total-bummer-pivotal-drops">total bummer that Pivotal drops Groovy</a></li>
<li>Koshuke Kawaguchi shares his view that the <a href="http://kohsuke.org/2015/01/20/groovy-project-should-have-a-clear-governance-structure/">Groovy project should have a clear governance structure</a></li>
<li>Mike Miller detects a <a href="http://programmingitch.blogspot.dk/2015/01/i-detect-disturbance-in-groovy-grails.html">disturbance in the (Groovy &amp; Grails) force</a></li>
<li>Groovy and Grails, <a href="http://www.intelligrape.com/blog/groovy-and-grails-gr8-without-pivotal/">GR8 without Pivotal</a> writes IntelliGrape&rsquo;s Himanshu Seth</li>
<li>Craig Burke worked on a <a href="http://www.craigburke.com/2015/01/22/groovy-document-builder.html">document builder to create Word and PDF documents in Groovy</a></li>
<li>The <a href="http://beta.groovy-lang.org/releasenotes/groovy-2.4.html">Groovy 2.4 release notes</a></li>
<li>The <a href="http://beta.groovy-lang.org/changelog-2.4.0.html">Groovy 2.4 changelog</a> now up on the new Groovy website</li>
<li>A Quora question: &ldquo;<a href="https://www.quora.com/Pivotal-is-no-longer-sponsoring-Grails-I-have-a-Grails-project-in-the-works-What-should-I-do">I&rsquo;ve started a Grails project, what should I do?</a>&rdquo;</li>
<li>Another Quora question: <a href="https://www.quora.com/Why-is-Pivotal-ending-the-sponsorship-of-Groovy-and-Grails">why is Pivotal ending sponsorship of Groovy and Grails</a>?</li>
<li>MrHaki&rsquo;s Groovy goodness: <a href="http://mrhaki.blogspot.fr/2015/01/groovy-goodness-take-or-drop-last-items.html">take or drop last items of a collection</a></li>
<li>On <a href="http://blog.robustastudio.com/mobile-development/android/building-multiple-editions-of-android-app-gradle/">building multiple editions of an Android application with Gradle</a> by Hassan Ibraheem</li>
<li><a href="https://www.voxxed.com/blog/2015/01/jvm-metrics-grails-awesomeness/">JVM metrics + Grails = awesomeness</a>, by Erwan Arzur</li>
<li>Yamila Moreno comes back on her Groovy 101 series with a post on <a href="http://moduslaborandi.net/groovy-101-gradle-travis-and-jenkins/">Groovy, GVM, Gradle, Travis and Jenkins</a></li>
<li>Using the <a href="http://www.perfectlearn.com/2015/01/how-i-used-the-cia-world-factbook-to-test-my-product/">CIA world fact book with Groovy</a></li>
<li>Let’s talk commitment: <a href="http://blog.ship.io/2015/01/23/lets-talk-commitment-gradle-and-version-control/">Gradle and version control</a>, by Tim Rosenblatt</li>
</ul>
<h2 id="podcast">Podcast</h2>
<ul>
<li>The Groovy podcast <a href="https://twitter.com/dailygrailstip/status/557943849251643393">interviewed Graeme Rocher and Guillaume Laforge</a></li>
</ul>
<h2 id="interviews">Interviews</h2>
<ul>
<li>Project lead <a href="http://jaxenter.com/grails-future-113958.html">Graeme Rocher on the future of Grails</a> on JaxEnter</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>A <a href="http://www.infoq.com/presentations/grails-3-preview">Grails 3.0 preview</a> by Graeme Rocher, recorded at SpringOne2GX 2014</li>
<li>The <a href="http://www.infoq.com/presentations/grails-introduction">quest for the holy Grails</a> by Ken Kousen, recorded at SpringOne2GX 2014</li>
<li><a href="http://www.infoq.com/presentations/grails-security-auth">Securing your Grails application, beyond authentication and authorization</a>, by Colin Harrington, recorded at SpringOne2GX 2014</li>
<li>David Carr&rsquo;s latest <a href="http://www.slideshare.net/davidmc24/intro-to-ratpack-cdjdn-2015">Ratpack introduction presentation</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>You can <a href="http://www.dzone.com/links/groovy_24_released_with_android_support.html">upvote this DZone news item about the release of Groovy 2.4</a></li>
<li>A fun little <a href="http://grooscript.org/demo/stars.html">demo of Groovy in the browser with GrooScript</a></li>
<li>Alcatel-Lucent&rsquo;s <a href="http://ecosystem.cloud-band.com/wp-content/uploads/2013/09/CloudBand-cPaas-Technical-Overview-1.6.pdf">PaaS solution leverages Groovy as its orchestration language</a></li>
<li>Andy Clement announces <a href="https://twitter.com/andy_clement/status/559061751736455169">Groovy Eclipse snapshots for Eclipse 4.4 have moved to Groovy 2.4</a></li>
<li><a href="https://github.com/ssadedin/graxxia">Graxxia, a mathematics library for Groovy</a>, wrapping commons-math, OpenCSV, GroovyCSV</li>
<li>Jacob Aae Mikkelsen <a href="http://grydeske.net/news/show/80">Grails Diary</a></li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>Dierk König is announcing that <a href="https://twitter.com/mittie/status/559841646654029824">Groovy in Action eventually goes to production</a>!</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>In <a href="https://gist.github.com/rmannibucau/e14324cdfc278ad388f1">24 lines of Groovy code to develop a JAX-RS service with OpenEJB</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>A Twitter <a href="https://twitter.com/alvaro_sanchez/status/559635311329026048">poll considers Google, Red Hat, Netflix and Gradleware as best sponsors</a> for the Groovy and Grails projects</li>
<li>Gradleware initiated a <a href="https://twitter.com/hashtag/WhyGradle?src=hash">twitter hashtag on &ldquo;why Gradle&rdquo;</a>, to which many contributed. Why do you use Gradle?</li>
<li>Dierk remarks that <a href="https://twitter.com/mittie/status/558011051313491969">Groovy 2.4 is as a compelling Android alternative to Java</a></li>
<li>Sergey Tselovalnikof noticed a <a href="https://twitter.com/SerCeMan/status/557960043380109312">funny little banner in Groovy&rsquo;s build</a>!</li>
<li><a href="https://twitter.com/gvmtool/status/557941663319203841">Groovy 2.4.0</a> available through GVM</li>
<li><a href="https://twitter.com/gvmtool/status/560101012623880192">Gradle 2.3-rc-1</a> available through GVM</li>
<li>Peter Ledbrook is looking forward to seeing the impact of Gradle&rsquo;s new configuration model</li>
<li>Cédric Champeau remarks that <a href="https://twitter.com/cedricchampeau/status/557822571543273473">positive comments outnumber FUD by an order of magniture</a></li>
<li>Cédric Champeau announces the <a href="https://twitter.com/CedricChampeau/status/558197359197560832">changes in versions in the Github branches</a> following up the release of Groovy 2.4</li>
<li><a href="https://twitter.com/mittie/status/558252300775731200">Performance improvements against Groovy 2.4</a> when Dierk König is running the self testing listings of Groovy in Action</li>
<li>Iván López thinks <a href="https://twitter.com/ilopmar/status/558274612287778816">Google should sponsor the Groovy and Grails projects</a>, especially now that Groovy 2.4 supports the Android platform</li>
<li>Florent Biville thinks <a href="https://twitter.com/fbiville/status/558977065710800896">since Groovy supports Android, Google should hire the Groovy and Grails teams</a></li>
<li>GradleWare offers two <a href="https://twitter.com/tomaslin/status/558806680599670784">free O&rsquo;Reilly e-books on Gradle</a></li>
<li>Erik Pragt shares a glimpse into the <a href="https://twitter.com/epragt/status/558983812655640576">new Asciidoctor-based Geb documentation</a></li>
<li>The <a href="http://rundis.github.io/blog/2015/gr_lt_status_jan2015.html">Groovy Light Table client is not dead</a> says Magnus Rundberget</li>
<li>Marco Vermeulen is making progress on the <a href="https://twitter.com/marc0der/status/559130196855771136">GVM security access for vendors</a></li>
<li>Antonio Goncalves tweets that a Maven <a href="https://twitter.com/agoncal/status/560001785654685696">pom.xml with the GMaven plugin and hundreds lines of Groovy code is probably by someone who wanted to use Gradle</a> but couldn&rsquo;t</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>The <a href="https://twitter.com/greachconf/status/559035001225101313">Greach organizers have received lots of great talk proposals</a></li>
<li><a href="https://twitter.com/springone2gx/status/559903903756861440">SpringOne2GX 2015 has been announced</a>, with 4 Groovy and Grails tracks, in Washington DC on Sept 14th-17th</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.4 released</title><link>https://glaforge.dev/posts/2015/01/21/groovy-2-4-released/</link><pubDate>Wed, 21 Jan 2015 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2015/01/21/groovy-2-4-released/</guid><description>&lt;p>The Groovy team is happy to announce the release of Groovy 2.4.0!&lt;/p>
&lt;p>The big highlight of this release is the Android support, which allows developers to write Android applications fully using Groovy, with much less boilerplate code than raw Java.&lt;/p>
&lt;p>The team also focused on various improvements in terms of performance, smaller bytecode generation, or memory consumption.&lt;/p>
&lt;p>Other interesting aspects worth noticing in this release are:&lt;/p>
&lt;ul>
&lt;li>traits can use the &lt;code>@SelfType&lt;/code> annotation with static type checking enabled to restrict to what classes traits can be applied&lt;/li>
&lt;li>GDK methods improvements&lt;/li>
&lt;li>some refinements to existing AST transformations&lt;/li>
&lt;li>further Groovysh improvements as well.&lt;/li>
&lt;/ul>
&lt;p>Please have a look at the &lt;a href="http://docs.codehaus.org/display/GROOVY/Groovy+2.4+release+notes">full release notes for Groovy 2.4&lt;/a> to know more about the new features and all the interesting tickets closed.&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy team is happy to announce the release of Groovy 2.4.0!</p>
<p>The big highlight of this release is the Android support, which allows developers to write Android applications fully using Groovy, with much less boilerplate code than raw Java.</p>
<p>The team also focused on various improvements in terms of performance, smaller bytecode generation, or memory consumption.</p>
<p>Other interesting aspects worth noticing in this release are:</p>
<ul>
<li>traits can use the <code>@SelfType</code> annotation with static type checking enabled to restrict to what classes traits can be applied</li>
<li>GDK methods improvements</li>
<li>some refinements to existing AST transformations</li>
<li>further Groovysh improvements as well.</li>
</ul>
<p>Please have a look at the <a href="http://docs.codehaus.org/display/GROOVY/Groovy+2.4+release+notes">full release notes for Groovy 2.4</a> to know more about the new features and all the interesting tickets closed.</p>
<p>You can have a look at the <a href="http://beta.groovy-lang.org/changelog-2.4.0.html">2.4 changelog</a> on the new Groovy website too.</p>
<p>And then, just go <a href="http://beta.groovy-lang.org/download.html">grab this release</a> while it&rsquo;s hot!</p>
<p>Thanks a lot to all those who contributed to this release, whether through bug reports, but also with documentation or code contributions through pull requests. All your help is warmly welcome!</p>
<p>Your support of Groovy and its ecosystem is what makes Groovy so strong, and what will allow it to continue making us all more productive for the next decade!</p>
<p>Keep on groovy&rsquo;ing!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #54</title><link>https://glaforge.dev/posts/2015/01/20/groovy-weekly-54/</link><pubDate>Tue, 20 Jan 2015 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2015/01/20/groovy-weekly-54/</guid><description>&lt;p>This week’s edition of Groovy Weekly is a bit peculiar, as the big news of the week is &lt;a href="http://blog.pivotal.io/pivotal/news-2/groovy-2-4-and-grails-3-0-to-be-last-major-releases-under-pivotal-sponsorship">Pivotal’s announcement that it will stop funding the Groovy and Grails&lt;/a> projects.&lt;/p>
&lt;p>As a result those popular Open Source projects are now seeking a new home!&lt;/p>
&lt;p>You can read &lt;a href="https://glaforge.dev/posts/2015/01/19/the-groovy-project-is-looking-for-a-new-home/">Guillaume Laforge’s&lt;/a> and &lt;a href="http://grails.io/post/108534902333/the-future-of-groovy-grails-sponsorship">Graeme Rocher’s&lt;/a> announcements to hear a bit more about the story. And if your company is interested in further funding the projects, please don’t hesitate to &lt;a href="mailto:sponsorship@groovy-lang.org">get in touch&lt;/a>!&lt;/p></description><content:encoded>
<![CDATA[<p>This week’s edition of Groovy Weekly is a bit peculiar, as the big news of the week is <a href="http://blog.pivotal.io/pivotal/news-2/groovy-2-4-and-grails-3-0-to-be-last-major-releases-under-pivotal-sponsorship">Pivotal’s announcement that it will stop funding the Groovy and Grails</a> projects.</p>
<p>As a result those popular Open Source projects are now seeking a new home!</p>
<p>You can read <a href="https://glaforge.dev/posts/2015/01/19/the-groovy-project-is-looking-for-a-new-home/">Guillaume Laforge’s</a> and <a href="http://grails.io/post/108534902333/the-future-of-groovy-grails-sponsorship">Graeme Rocher’s</a> announcements to hear a bit more about the story. And if your company is interested in further funding the projects, please don’t hesitate to <a href="mailto:sponsorship@groovy-lang.org">get in touch</a>!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://twitter.com/cedricchampeau/status/555014971814576129">Gradle Groovy Android plugin 0.3.5</a> updated with new coordinates</li>
<li>The axion-release-plugin hits 1.0, a <a href="http://allegrotech.io/axion-release-plugin.html">&ldquo;release&rdquo; plugin for Gradle</a></li>
<li><a href="https://twitter.com/bsideup/status/556820732576272385">Groovy-Macro-Methods 0.3.0</a> with huge optimizations</li>
<li><a href="https://twitter.com/springcentral/status/557245887051603969">Groovy Grails Tool Suite 3.6.3 SR1</a> released fixing a security vulnerability</li>
<li><a href="https://twitter.com/grainframework/status/557452579819360257">Grain 0.6.3</a> released with updated Asciidoctor</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li><a href="http://blog.pivotal.io/pivotal/news-2/groovy-2-4-and-grails-3-0-to-be-last-major-releases-under-pivotal-sponsorship">Pivotal announces the end of the funding of the Groovy and Grails</a> projects</li>
<li><a href="https://glaforge.dev/posts/2015/01/19/the-groovy-project-is-looking-for-a-new-home/">Guillaume Laforge’s announcement</a> about Pivotal’s end of sponsorship of the Groovy and Grails projects</li>
<li><a href="http://grails.io/post/108534902333/the-future-of-groovy-grails-sponsorship">Graeme Rocher’s announcement</a> regarding the end of the funding of the Groovy and Grails projects by Pivotal</li>
<li><a href="https://docs.google.com/a/pivotal.io/document/d/1R_bQv_8JSLOEzVzPdqOISF0guGtcUPE6vFBeAuxtcG4/edit">Pivotal&rsquo;s FAQ on the end of sponsorship</a> of the Groovy and Grails projects</li>
<li>Cédric Champeau wrote a guest blog post on TeamCity&rsquo;s blog about <a href="http://blog.jetbrains.com/teamcity/2015/01/how-groovy-uses-teamcity/">how Groovy uses TeamCity</a></li>
<li><a href="http://melix.github.io/blog/2015/01/for-hire.html">Cédric Champeau of Groovy fame would love to continue to work on Groovy full time, but he&rsquo;s soon gonna be for hire otherwise</a>, catch him if you get a chance!</li>
<li>Ken Kousen’s lovely post on <a href="https://kousenit.wordpress.com/2015/01/19/groovygrails-pivotal-opportunity/">why Groovy and Grails community matters</a>, opportunities ahead!</li>
<li>Peter Ledbrook details <a href="http://blog.cacoethes.co.uk/software/why-gradle">why you should use Gradle</a></li>
<li><a href="http://redmonk.com/sogrady/2015/01/14/language-rankings-1-15/">Groovy listed in the top 20 of RedMonk&rsquo;s programming language rankings</a> of January 2015</li>
<li><a href="http://www.objectpartners.com/2015/01/13/reset-your-h2-database-for-a-clean-state-between-functional-tests/">Reset your H2 database for a clean state between functional tests</a> with the help of Spock and Groovy traits</li>
<li>A lively imaginary <a href="http://blog.cleancoder.com/uncle-bob/2015/01/08/InterfaceConsideredHarmful.html">discussion on interfaces, abstract classes, multiple inheritance</a> by Uncle Bob, which may make you think Groovy traits might be the answer</li>
<li>The <a href="http://code.tutsplus.com/tutorials/the-ins-and-outs-of-gradle--cms-22978">ins and outs of Gradle</a>, by Jessica Thornsby, from an Android perspective</li>
<li>Voxxed published a master-post to <a href="https://www.voxxed.com/blog/2015/01/thinking-about-picking-up-a-new-jvm-language-a-masterpost-to-guide-java-devs/">guide Java developers choosing a new JVM language</a>, including Groovy in the list</li>
<li><a href="https://www.virag.si/2015/01/publishing-gradle-android-library-to-jcenter/">Publishing Gradle Android library to JCenter repository</a> by Jernej Virag</li>
<li>Anton Arhipov thinks what’s happening could be a <a href="http://arhipov.blogspot.fr/2015/01/g.html">new beginning for Groovy and Grails</a></li>
</ul>
<h2 id="interviews">Interviews</h2>
<ul>
<li>Victor Grazi <a href="http://www.infoq.com/news/2015/01/Pivotal-Pulls-Groovy-Grails-Fund">interviewed Guillaume Laforge on Pivotal&rsquo;s decision</a> with regards to Groovy and Grails</li>
<li>Guillaume Laforge interviewed by Voxxed on the “<a href="https://www.voxxed.com/blog/2015/01/sad-odd-decision-pivotal-set-groovy-adrift/">sad and odd decision by Pivotal to set Groovy adrift</a>”</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>InfoQ SpringOne2GX coverage:
<ul>
<li><a href="http://www.infoq.com/presentations/groovy-2-3-api-design">Rethinking API design with traits</a>, by Cédric Champeau</li>
<li><a href="http://www.infoq.com/presentations/groovy-test-java-spock">Testing Java, Groovy, Spring and web applications with Spock</a>, by Peter Niederwieser</li>
<li><a href="http://www.infoq.com/presentations/bigdata-storm-groovy">Scalable Big Data stream processing with Storm and Groovy</a>, by Eugene Dvorkin</li>
</ul>
</li>
<li>SpringOne2GX 2014 replay from the Spring.IO blog
<ul>
<li><a href="https://spring.io/blog/2015/01/09/springone2gx-2014-replay-how-to-get-groovy-with-java-8">How to get Groovy with Java 8</a> by Peter Ledbrook</li>
<li><a href="https://spring.io/blog/2015/01/08/springone2gx-2014-replay-android-and-groovy-a-winning-pair">Android and Groovy, a winning pair?</a> by Cédric Champeau</li>
<li><a href="https://spring.io/blog/2015/01/08/springone2gx-2014-replay-experiences-using-grails-in-a-microservice-architecture">Experiences using Grails in a microservice architecture</a> by Jeff Beck</li>
<li>Dan Woods <a href="http://fr.slideshare.net/danveloper/ratpack-web-framework-from-oreilly-webcast-20150113">introduction to Ratpack</a> in this O&rsquo;Reilly webcast</li>
</ul>
</li>
<li>Slides of the Grails Centro meetup on the <a href="https://www.slideshare.net/fullscreen/bobdobbes/silicon-valley-grails/1">Grails API toolkit</a> by Owen Rubel</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>All the <a href="https://github.com/grails/grails-core/releases">Grails release notes are now available on Github</a></li>
<li><a href="http://www.eclecticlogic.com/pedal-loader/">Pedal-Loader is a Groovy DSL for data loading</a> and an attractive substitute to db-unit</li>
<li>A work-in-progress of the <a href="http://grails.github.io/grails-static-website/">future Grails static web site</a>, forked from Groovy&rsquo;s new website</li>
<li>The <a href="http://grooscript.org/doc.html">GrooScript&rsquo;s documentation</a> is almost finished, any feedback?</li>
<li>Jacob Aae Mikkelsen’s <a href="http://grydeske.net/news/show/79">Grails Diary</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>A <a href="https://gist.github.com/chiquitinxx/6ef000456732aa9bd7fb">single page interface with Groovy traits, templates, builders and GrooScript</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Stephan Janssen, founder of Devoxx predicts that <a href="https://twitter.com/Stephan007/status/557459062221455364">2015 will be the year Red Hat hires the Groovy and Grails teams</a> and embraces their communities</li>
<li>Latest <a href="https://twitter.com/cedricchampeau/status/555015335313932289">Gradle Groovy Android plugin available on Bintray and the Gradle plugin portal</a></li>
<li>Guillaume Laforge&rsquo;s <a href="https://twitter.com/glaforge/status/556027163494084608">duck typing joke in the age of quantum computing</a></li>
<li>Rohit Kelapure answers yes to the question of <a href="https://twitter.com/rkela/status/555079299074916353">pushing Ratpack apps on Cloud Foundry out of the box</a></li>
<li><a href="https://twitter.com/grooscript/status/556466614259154944">GrooScript is aiming a 1.0 release</a> by the end of January</li>
<li>Arnaud Héritier is supporting Groovy saying &ldquo;<a href="https://twitter.com/aheritier/status/557138343914655744">Je Suis Groovy</a>&rdquo;, in echo to recent tragic terrorists attacks in France</li>
<li>Jeff Brown reminds us that the <a href="https://twitter.com/jeffscottbrown/status/557196649916022784">main focus for the Groovy and Grails teams are to secure the technologies&rsquo; future</a></li>
<li>Burt Beckwith suggesting <a href="https://twitter.com/burtbeckwith/status/557220977437081600">being fast to get a chance to snap the talented Groovy and Grails teams</a>!</li>
<li>Andy Wilkinson reminds us <a href="https://twitter.com/ankinson/status/557457888160280576">some developers believe that software costs nothing to produce</a></li>
<li>Dean Iverson notes that the <a href="https://twitter.com/deanriverson/status/557302834815700992">Pivotal&rsquo;s divesting doesn&rsquo;t make Groovy any less useful</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>Jochen Theodorou will be the Groovy keynote speaker of Greach 2015</li>
<li><a href="https://twitter.com/greachconf/status/556021832747192321">Salenda is a new gold sponsor of the Greach</a> conference</li>
<li><a href="https://twitter.com/greachconf/status/556031871260192768">Burt Beckwith will be speaking at the Greach</a> conference</li>
<li>GR8Ladies founder <a href="https://twitter.com/greachconf/status/554964855027818497">Jane Stratter will be speaking at Greach</a></li>
<li>Still <a href="https://twitter.com/gr8conf/status/556244984177766400">curious about the GR8Conf conference</a>? Check the videos!</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>The Groovy project is looking for a new home</title><link>https://glaforge.dev/posts/2015/01/19/the-groovy-project-is-looking-for-a-new-home/</link><pubDate>Mon, 19 Jan 2015 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2015/01/19/the-groovy-project-is-looking-for-a-new-home/</guid><description>&lt;p>Pivotal just announced it’s decision &lt;a href="http://blog.pivotal.io/pivotal/news-2/groovy-2-4-and-grails-3-0-to-be-last-major-releases-under-pivotal-sponsorship">to stop sponsoring and funding the development of the popular Groovy and Grails Open Source projects&lt;/a>. As a result, both Groovy and Grails are looking for new sponsors willing to further help develop the projects full steam!&lt;/p>
&lt;p>The &lt;a href="http://groovy-lang.org">Groovy programming language&lt;/a> has been around for a while for more than 11 years. During that time, it has nicely evolved from a side hobby project to the very mature and successful alternative language it is today, used by Fortune 500 companies throughout the world, in various projects and contexts.&lt;/p></description><content:encoded>
<![CDATA[<p>Pivotal just announced it’s decision <a href="http://blog.pivotal.io/pivotal/news-2/groovy-2-4-and-grails-3-0-to-be-last-major-releases-under-pivotal-sponsorship">to stop sponsoring and funding the development of the popular Groovy and Grails Open Source projects</a>. As a result, both Groovy and Grails are looking for new sponsors willing to further help develop the projects full steam!</p>
<p>The <a href="http://groovy-lang.org">Groovy programming language</a> has been around for a while for more than 11 years. During that time, it has nicely evolved from a side hobby project to the very mature and successful alternative language it is today, used by Fortune 500 companies throughout the world, in various projects and contexts.</p>
<p>With 1.7 million downloads in 2012, 3 million in 2013, and well over 4 million in 2014 (definitive numbers still need to be calculated), Groovy is leading the pack of the JVM language ecosystem, and continues seeing positive growth.</p>
<p>There are many ideas the Groovy team wants to develop further, features we want to bring to life, improvements we want to make, to keep Groovy always ahead of the curve, to help you developers be productive on the JVM platform. For that, we’ve been thankful for having had a handful of us able to work full time on the project, and we’re looking forward continuing to do so under a new umbrella.</p>
<p>The Groovy community has always been a key driver for the language, providing feedback, bug reports, contributions big and small, and we hope that you will help us find a solution to make Groovy shine as bright as ever.</p>
<p>Of course, we’re going to continue to develop Groovy, open it to new horizons like we did for the Android platform, implement new features, fix bugs, increase performance, complete the new documentation, launch the new website, and more. Your contributions will obviously be more than welcome to sustain the project’s pace. We’re looking forward to working with you all to push Groovy forward!</p>
<p>If your company is interested in discussing funding of the project, and employing members of the Groovy team, please don’t hesitate to contact us directly (<a href="mailto:sponsorship@groovy-lang.org">sponsorship@groovy-lang.org</a>). Thanks in advance for your help, and keep on groovy’ing!</p>
<p><em>Update:</em> <a href="http://grails.io/post/108534902333/the-future-of-groovy-grails-sponsorship">Graeme&rsquo;s blog post</a> about the announcement</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #53</title><link>https://glaforge.dev/posts/2015/01/13/groovy-weekly-53/</link><pubDate>Tue, 13 Jan 2015 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2015/01/13/groovy-weekly-53/</guid><description>&lt;p>The Groovy development team released the second release candidate of Groovy 2.4, please be sure to check your applications with this version and report any issue you may be finding, so that we can all ensure 2.4 is a rock-solid release.&lt;/p>
&lt;p>Note the publication of MrHaki’s &lt;a href="http://mrhaki.blogspot.fr/2015/01/gradle-goodness-notebook-is-published.html">Gradle Goodness Notebook&lt;/a>! If you’re using Gradle, this is worth the investment to have that resource handy for mastering your build automation.&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://groovy.329449.n5.nabble.com/ANN-Second-release-candidate-for-Groovy-2-4-td5722110.html">Groovy 2.4 second release candidate&lt;/a>, please help testing it!&lt;/li>
&lt;li>&lt;a href="https://spring.io/blog/2015/01/08/spring-boot-1-2-1-released">Spring Boot 1.2.1&lt;/a> released&lt;/li>
&lt;li>The &lt;a href="http://beta.groovy-lang.org/download.html">Windows installer for Groovy 2.3.9&lt;/a> is available&lt;/li>
&lt;li>&lt;a href="https://twitter.com/grooscript/status/553626428772995072">Grooscript Gradle plugin 0.9&lt;/a> released&lt;/li>
&lt;li>Groovy Android library &lt;a href="https://github.com/Arasthel/SwissKnife/blob/master/CHANGELOG.md">SwissKnife 1.2&lt;/a> released with @Parcelable transformation&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>Jochen &amp;ldquo;Blackdrag&amp;rdquo; Theodorou behind the scenes of &lt;a href="http://blackdragsview.blogspot.fr/2015/01/indy-and-compilestatic-as-tag-team-to.html">bytecode optimizations for improving array access performance&lt;/a>&lt;/li>
&lt;li>Doug Borg describes what &lt;a href="http://dougborg.org/what-makes-a-good-build-dot-gradle">makes a good build.gradle&lt;/a>&lt;/li>
&lt;li>Reinout Korbee writes about &lt;a href="http://java.dzone.com/articles/run-your-antlr-dsl-groovy">running your Antlr DSL as a Groovy script&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://arzur.net/octopress/blog/2015/01/07/groovy-magic/">Some Groovy magic&lt;/a> by Erwan Arzur&lt;/li>
&lt;li>&lt;a href="http://blog.jdriven.com/2015/01/grails-preventing-naming-collisions/">Preventing naming collisions in Grails&lt;/a> by Albert van Veen&lt;/li>
&lt;li>Bertrand Goetzman shows how to display data coming from &lt;a href="http://www.odelia-technologies.com/content/afficher-des-donnees-provenant-dune-source-de-donnees-rest-dans-une-application-javafx-avec">REST with DataFX / JavaFX and Ratpack&lt;/a> (article in French)&lt;/li>
&lt;li>Robert McIntosh on &lt;a href="http://robertmcintosh.me/blog/2015/grails_with_billion_record_mongo_collection.html">Grails with a billion record Mongo collection&lt;/a>&lt;/li>
&lt;li>Peter Ledbrook on &lt;a href="http://blog.cacoethes.co.uk/groovyandgrails/groovy-in-light-of-java-8">Groovy in the light of Java 8&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.intelligrape.com/blog/the-biggest-grails-conference-in-india-grailsconf-2015/">Feedback from GrailsConf in India&lt;/a> by Amit Jain&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>Dan Woods will be speaking about &lt;a href="https://twitter.com/ratpackweb/status/552796932049883137">Ratpack online during an O&amp;rsquo;Reilly webinar&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.infoq.com/presentations/groovy-2-3">Groovy in 2014 and beyond&lt;/a> by Guillaume Laforge at SpringOne2GX 2014&lt;/li>
&lt;li>&lt;a href="http://www.infoq.com/presentations/groovy-grails-spring-boot">Making Spring Boot even groovier&lt;/a> by Graeme Rocher at SpringOne2GX 2014&lt;/li>
&lt;li>Slides form &amp;ldquo;&lt;a href="https://speakerdeck.com/kdabir/demystifying-gradle-dsl">Demystifying Gradle DSL&lt;/a>&amp;rdquo; presented at GrailsConf 2015, New Delhi&lt;/li>
&lt;/ul>
&lt;h2 id="interviews">Interviews&lt;/h2>
&lt;ul>
&lt;li>Guillaume Laforge interviewed at Devoxx 2014 about what makes &lt;a href="https://www.voxxed.com/blog/2015/01/groovy-swift-android-world/">Groovy the Swift of the Android world&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="books">Books&lt;/h2>
&lt;ul>
&lt;li>MrHaki publishes the &lt;a href="http://mrhaki.blogspot.fr/2015/01/gradle-goodness-notebook-is-published.html">Gradle Goodness Notebook&lt;/a>&lt;/li>
&lt;li>Marco Vermeulen enjoys the &lt;a href="https://twitter.com/marc0der/status/554254484013330432">terrific section on writing nested Gradle plugin extension DSLs&lt;/a> in Tim Berglund&amp;rsquo;s &lt;a href="http://shop.oreilly.com/product/0636920019923.do">Gradle beyond the basics&lt;/a> book&lt;/li>
&lt;/ul>
&lt;h2 id="news">News&lt;/h2>
&lt;ul>
&lt;li>Jacob Aae Mikkelsen &lt;a href="http://grydeske.net/news/show/78">Grails Diary&lt;/a>&lt;/li>
&lt;li>Testatoo, a web &lt;a href="https://github.com/Ovea/testatoo/blob/2.0.b3/src/doc/testatoo.adoc">user interface testing tool with a Groovy DSL&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="mailing-list">Mailing-list&lt;/h2>
&lt;ul>
&lt;li>A debate about &lt;a href="http://groovy.329449.n5.nabble.com/JVM-application-installer-via-maven-gradle-repo-td5722094.html">JVM application installers with Gradle&lt;/a> and more&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>Iván Lopez participates in the Spring Boot book contest with a &lt;a href="http://lmivan.github.io/contest/#_technological_stack">full Groovy solution&lt;/a>! Star the project to make it win!&lt;/li>
&lt;li>Dan Woods shares some &lt;a href="https://twitter.com/danveloper/status/554008783077134336">examples of standalone Ratpack&lt;/a> 0.9.13&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>Guillaume Laforge celebrated the &lt;a href="https://twitter.com/glaforge/status/552492079386198020">1000th star on Github for Groovy&lt;/a>, keep on spreading the word and double that number!&lt;/li>
&lt;li>&lt;a href="https://twitter.com/glaforge/status/552492370768723968">Groovy Weekly released its 2000th news item&lt;/a> last week!&lt;/li>
&lt;li>&lt;a href="https://twitter.com/ratpackweb/status/552796345749082112">Ratpack is teasing that the next release&lt;/a> is going to be a big &amp;ldquo;one&amp;rdquo;&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/552825902514847744">Groovy 2.4.0-rc-2&lt;/a> available through GVM&lt;/li>
&lt;li>Cédric Champeau asks the twittosphere what they&amp;rsquo;d like to &lt;a href="https://twitter.com/cedricchampeau/status/553107314182717440">see him speak about at GR8Conf&lt;/a>&lt;/li>
&lt;li>Andrew Reitz managed to get &lt;a href="https://twitter.com/andrewreitz_/status/553301907322576896">Spock tests running on Android&lt;/a>&lt;/li>
&lt;li>Vishal Savajiani claims &lt;a href="https://twitter.com/vishalgreat/status/553296725025120258">Groovy is a powerful language&lt;/a>! For the Internet of Things, for the Cloud, and more.&lt;/li>
&lt;li>The Groovy Android library &lt;a href="https://twitter.com/eugenekamenev/status/554716837720915969">SwissKnife is ready for Groovy 2.4&lt;/a>, reports Eugene Kamenev&lt;/li>
&lt;li>Mike Milinkovich is excited to see &lt;a href="https://twitter.com/mmilinkov/status/553556027913031680">Gradleware bringing first-class Gradle support to Eclipse&lt;/a>&lt;/li>
&lt;li>Iván López demonstrates you can be a &lt;a href="https://twitter.com/ilopmar/status/554219468843270144">full stack developer using only Groovy&lt;/a> with a concrete example&lt;/li>
&lt;li>&lt;a href="https://twitter.com/ankinson/status/554609893395927040">Spring Boot is ready for Groovy 2.4&lt;/a> announces Andy Wilkinson&lt;/li>
&lt;li>Dan Woods compares the &lt;a href="https://twitter.com/danveloper/status/554576240833986560">performance of Ratpack with Vert.x&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://devnexus.com/s/presentations?tags=groovy">Groovy at DevNexus&lt;/a>, with an epic keynote with the Groovy puzzlers and a Groovy workshop, by the JFrog team&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/greachconf/status/553125994114994176">Greach Call for Paper is closing in 10 days&lt;/a>, get ready!&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gr8conf/status/552507512323600386">Last week for the GR8Conf Call for Paper&lt;/a>, hurry up!&lt;/li>
&lt;li>Cédric Champeau will speak about &lt;a href="https://twitter.com/poitoujug/status/552767245357944832">Groovy at Niort for the Poitou-Charentes JUG&lt;/a> on January 20th&lt;/li>
&lt;li>Greach announces its first accepted speakers: &lt;a href="https://twitter.com/greachconf/status/554937171027574784">Russel Winder&lt;/a>&lt;/li>
&lt;li>Only &lt;a href="https://twitter.com/greachconf/status/554952362042097665">2 days left for the early bird tickets for Greach&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/greachconf/status/554582324973539332">Dan Woods will be speaking at Greach&lt;/a> about Ratpack&lt;/li>
&lt;li>Another speaker announced for &lt;a href="https://twitter.com/greachconf/status/554597468977901569">Greach with MrHaki&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/GR8ConfUS/status/552672571754569728">GR8Conf US announces multiple tracks for the workshop day&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>The Groovy development team released the second release candidate of Groovy 2.4, please be sure to check your applications with this version and report any issue you may be finding, so that we can all ensure 2.4 is a rock-solid release.</p>
<p>Note the publication of MrHaki’s <a href="http://mrhaki.blogspot.fr/2015/01/gradle-goodness-notebook-is-published.html">Gradle Goodness Notebook</a>! If you’re using Gradle, this is worth the investment to have that resource handy for mastering your build automation.</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://groovy.329449.n5.nabble.com/ANN-Second-release-candidate-for-Groovy-2-4-td5722110.html">Groovy 2.4 second release candidate</a>, please help testing it!</li>
<li><a href="https://spring.io/blog/2015/01/08/spring-boot-1-2-1-released">Spring Boot 1.2.1</a> released</li>
<li>The <a href="http://beta.groovy-lang.org/download.html">Windows installer for Groovy 2.3.9</a> is available</li>
<li><a href="https://twitter.com/grooscript/status/553626428772995072">Grooscript Gradle plugin 0.9</a> released</li>
<li>Groovy Android library <a href="https://github.com/Arasthel/SwissKnife/blob/master/CHANGELOG.md">SwissKnife 1.2</a> released with @Parcelable transformation</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Jochen &ldquo;Blackdrag&rdquo; Theodorou behind the scenes of <a href="http://blackdragsview.blogspot.fr/2015/01/indy-and-compilestatic-as-tag-team-to.html">bytecode optimizations for improving array access performance</a></li>
<li>Doug Borg describes what <a href="http://dougborg.org/what-makes-a-good-build-dot-gradle">makes a good build.gradle</a></li>
<li>Reinout Korbee writes about <a href="http://java.dzone.com/articles/run-your-antlr-dsl-groovy">running your Antlr DSL as a Groovy script</a></li>
<li><a href="http://arzur.net/octopress/blog/2015/01/07/groovy-magic/">Some Groovy magic</a> by Erwan Arzur</li>
<li><a href="http://blog.jdriven.com/2015/01/grails-preventing-naming-collisions/">Preventing naming collisions in Grails</a> by Albert van Veen</li>
<li>Bertrand Goetzman shows how to display data coming from <a href="http://www.odelia-technologies.com/content/afficher-des-donnees-provenant-dune-source-de-donnees-rest-dans-une-application-javafx-avec">REST with DataFX / JavaFX and Ratpack</a> (article in French)</li>
<li>Robert McIntosh on <a href="http://robertmcintosh.me/blog/2015/grails_with_billion_record_mongo_collection.html">Grails with a billion record Mongo collection</a></li>
<li>Peter Ledbrook on <a href="http://blog.cacoethes.co.uk/groovyandgrails/groovy-in-light-of-java-8">Groovy in the light of Java 8</a></li>
<li><a href="http://www.intelligrape.com/blog/the-biggest-grails-conference-in-india-grailsconf-2015/">Feedback from GrailsConf in India</a> by Amit Jain</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Dan Woods will be speaking about <a href="https://twitter.com/ratpackweb/status/552796932049883137">Ratpack online during an O&rsquo;Reilly webinar</a></li>
<li><a href="http://www.infoq.com/presentations/groovy-2-3">Groovy in 2014 and beyond</a> by Guillaume Laforge at SpringOne2GX 2014</li>
<li><a href="http://www.infoq.com/presentations/groovy-grails-spring-boot">Making Spring Boot even groovier</a> by Graeme Rocher at SpringOne2GX 2014</li>
<li>Slides form &ldquo;<a href="https://speakerdeck.com/kdabir/demystifying-gradle-dsl">Demystifying Gradle DSL</a>&rdquo; presented at GrailsConf 2015, New Delhi</li>
</ul>
<h2 id="interviews">Interviews</h2>
<ul>
<li>Guillaume Laforge interviewed at Devoxx 2014 about what makes <a href="https://www.voxxed.com/blog/2015/01/groovy-swift-android-world/">Groovy the Swift of the Android world</a></li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>MrHaki publishes the <a href="http://mrhaki.blogspot.fr/2015/01/gradle-goodness-notebook-is-published.html">Gradle Goodness Notebook</a></li>
<li>Marco Vermeulen enjoys the <a href="https://twitter.com/marc0der/status/554254484013330432">terrific section on writing nested Gradle plugin extension DSLs</a> in Tim Berglund&rsquo;s <a href="http://shop.oreilly.com/product/0636920019923.do">Gradle beyond the basics</a> book</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Jacob Aae Mikkelsen <a href="http://grydeske.net/news/show/78">Grails Diary</a></li>
<li>Testatoo, a web <a href="https://github.com/Ovea/testatoo/blob/2.0.b3/src/doc/testatoo.adoc">user interface testing tool with a Groovy DSL</a></li>
</ul>
<h2 id="mailing-list">Mailing-list</h2>
<ul>
<li>A debate about <a href="http://groovy.329449.n5.nabble.com/JVM-application-installer-via-maven-gradle-repo-td5722094.html">JVM application installers with Gradle</a> and more</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Iván Lopez participates in the Spring Boot book contest with a <a href="http://lmivan.github.io/contest/#_technological_stack">full Groovy solution</a>! Star the project to make it win!</li>
<li>Dan Woods shares some <a href="https://twitter.com/danveloper/status/554008783077134336">examples of standalone Ratpack</a> 0.9.13</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Guillaume Laforge celebrated the <a href="https://twitter.com/glaforge/status/552492079386198020">1000th star on Github for Groovy</a>, keep on spreading the word and double that number!</li>
<li><a href="https://twitter.com/glaforge/status/552492370768723968">Groovy Weekly released its 2000th news item</a> last week!</li>
<li><a href="https://twitter.com/ratpackweb/status/552796345749082112">Ratpack is teasing that the next release</a> is going to be a big &ldquo;one&rdquo;</li>
<li><a href="https://twitter.com/gvmtool/status/552825902514847744">Groovy 2.4.0-rc-2</a> available through GVM</li>
<li>Cédric Champeau asks the twittosphere what they&rsquo;d like to <a href="https://twitter.com/cedricchampeau/status/553107314182717440">see him speak about at GR8Conf</a></li>
<li>Andrew Reitz managed to get <a href="https://twitter.com/andrewreitz_/status/553301907322576896">Spock tests running on Android</a></li>
<li>Vishal Savajiani claims <a href="https://twitter.com/vishalgreat/status/553296725025120258">Groovy is a powerful language</a>! For the Internet of Things, for the Cloud, and more.</li>
<li>The Groovy Android library <a href="https://twitter.com/eugenekamenev/status/554716837720915969">SwissKnife is ready for Groovy 2.4</a>, reports Eugene Kamenev</li>
<li>Mike Milinkovich is excited to see <a href="https://twitter.com/mmilinkov/status/553556027913031680">Gradleware bringing first-class Gradle support to Eclipse</a></li>
<li>Iván López demonstrates you can be a <a href="https://twitter.com/ilopmar/status/554219468843270144">full stack developer using only Groovy</a> with a concrete example</li>
<li><a href="https://twitter.com/ankinson/status/554609893395927040">Spring Boot is ready for Groovy 2.4</a> announces Andy Wilkinson</li>
<li>Dan Woods compares the <a href="https://twitter.com/danveloper/status/554576240833986560">performance of Ratpack with Vert.x</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="http://devnexus.com/s/presentations?tags=groovy">Groovy at DevNexus</a>, with an epic keynote with the Groovy puzzlers and a Groovy workshop, by the JFrog team</li>
<li>The <a href="https://twitter.com/greachconf/status/553125994114994176">Greach Call for Paper is closing in 10 days</a>, get ready!</li>
<li><a href="https://twitter.com/gr8conf/status/552507512323600386">Last week for the GR8Conf Call for Paper</a>, hurry up!</li>
<li>Cédric Champeau will speak about <a href="https://twitter.com/poitoujug/status/552767245357944832">Groovy at Niort for the Poitou-Charentes JUG</a> on January 20th</li>
<li>Greach announces its first accepted speakers: <a href="https://twitter.com/greachconf/status/554937171027574784">Russel Winder</a></li>
<li>Only <a href="https://twitter.com/greachconf/status/554952362042097665">2 days left for the early bird tickets for Greach</a></li>
<li><a href="https://twitter.com/greachconf/status/554582324973539332">Dan Woods will be speaking at Greach</a> about Ratpack</li>
<li>Another speaker announced for <a href="https://twitter.com/greachconf/status/554597468977901569">Greach with MrHaki</a></li>
<li><a href="https://twitter.com/GR8ConfUS/status/552672571754569728">GR8Conf US announces multiple tracks for the workshop day</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #52</title><link>https://glaforge.dev/posts/2015/01/06/groovy-weekly-52/</link><pubDate>Tue, 06 Jan 2015 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2015/01/06/groovy-weekly-52/</guid><description>&lt;p>First of all, let me wish you a very Groovy year! May 2015 fulfill all its promises, keep you healthy, bring joy and hapiness around! And of course, may it be filled with tons of bits of Groovy code!&lt;/p>
&lt;p>Following up my plea for &lt;a href="https://github.com/groovy/groovy-core">more stars on Github&lt;/a>, we went from below 600 stars to beyond 1000! But we don’t need to stop there, so let’s keep starring and spreading the word!&lt;/p></description><content:encoded>
<![CDATA[<p>First of all, let me wish you a very Groovy year! May 2015 fulfill all its promises, keep you healthy, bring joy and hapiness around! And of course, may it be filled with tons of bits of Groovy code!</p>
<p>Following up my plea for <a href="https://github.com/groovy/groovy-core">more stars on Github</a>, we went from below 600 stars to beyond 1000! But we don’t need to stop there, so let’s keep starring and spreading the word!</p>
<p>Another thousand milestone reached with the 2000th news item delivered in Groovy Weekly this week!</p>
<p>Remember that you can <a href="http://bit.ly/groovy-weekly-subscribe">subscribe</a> to this Groovy Weekly series to get those news directly in your inbox, and that you can also <a href="http://bit.ly/groovyweekly">contribute news</a> yourself by filling a form if there’s something you want to tell the world!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://twitter.com/grooscript/status/552051531399577600">Grooscript 1.0-rc-1</a>, be sure to test it before the final release!</li>
<li><a href="http://www.ratpack.io/versions/0.9.12">Ratpack 0.9.12</a> released</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>All in Together: <a href="http://www.bignerdranch.com/blog/all-in-together-android-studio-gradle-and-robolectric/">Android Studio, Gradle and Robolectric</a> by Jason Atwood</li>
<li><a href="http://groovymn.tumblr.com/post/106798263197/grails-angular-phantomjs-pdf-reports">Grails + Angular + PhantomJS = PDF Reports</a></li>
<li><a href="http://groovymn.tumblr.com/post/107130385197/grails-demo-pdf-app">Grails Demo PDF app</a></li>
<li>Second part on <a href="http://www.intelligrape.com/blog/grails-unique-constraint-optimization-part-2/">Grails unique constraint optimization</a> by Deepak Kumar Mittal</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li><a href="http://www.infoq.com/presentations/Groovy-ea-ops">Groovy for system administrators</a>, by Dan Woods, recorded at SpringOne2GX 2014</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>GPars 1.3 will feature a new Asciidoctor-based <a href="http://gpars.org/SNAPSHOT/aguide/html5/index.html">GPars user guide</a></li>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="http://grydeske.net/news/show/77">Grails Diary</a> week 51</li>
<li>This <a href="http://groovymn.tumblr.com/post/106705639062/this-month-in-gum">month in GUM</a></li>
</ul>
<h2 id="mailing-list">Mailing-list</h2>
<ul>
<li>Interesting dicussion on using <a href="http://groovy.329449.n5.nabble.com/Groovy-for-data-science-a-killer-application-for-Groovy-td5722061.html">Groovy for data science, as a killer application for Groovy</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Cédric Champeau is musing with ideas of how to <a href="https://gist.github.com/melix/e27dce2c23467d47e7f4#file-cli-friendly-groovy-L10">improve Groovy command-line interactions</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>You can contribute to Grooscript by going through the <a href="https://twitter.com/grooscript/status/552052096183590912">Grooscript documentation</a></li>
<li>Andrew Reitz finds using <a href="https://twitter.com/andrewreitz_/status/552147730550644737">unit testing Android more bearable with Groovy</a></li>
<li>The Groovy team builds <a href="https://twitter.com/CedricChampeau/status/550982802272178178">Groovy on Continuous Integration with JDK 9 Jigsaw</a> too</li>
<li>Russell Hart explains how to <a href="https://twitter.com/rus_hart/status/551019292805070849">make authenticated user details available to Ratpack view model</a></li>
<li>Guillaume Laforge got a <a href="https://twitter.com/glaforge/status/552159421292630016">groovy electric guitar</a> for Christmas&hellip; or was it for his daughter?</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="https://plus.google.com/u/0/events/ceh2qfaalqv3vgnhq8tjoihjnic">10 languages in 10 minutes (including Groovy)</a> at the Coder Consortium in Sacramento on February 5th</li>
<li><a href="https://twitter.com/tbsalling/status/551979427924807680">GR8Conf Europe is looking for sponsors</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #51</title><link>https://glaforge.dev/posts/2014/12/30/groovy-weekly-51/</link><pubDate>Tue, 30 Dec 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/12/30/groovy-weekly-51/</guid><description>&lt;p>Here’s the end of a pretty Groovy year coming, giving room for an even groovier one!&lt;/p>
&lt;p>I hope you all enjoyed the holidays, and that you’re ready to pour in some more drops of Groovy into your software mixes!&lt;/p>
&lt;p>Don’t forget to continue &lt;a href="https://github.com/groovy/groovy-core">starring the Groovy project on Github&lt;/a>, if you haven’t done so, so we try to reach 1000 in the new year!&lt;/p>
&lt;p>What are going to be your new year’s resolutions? Perhaps some contributions to Groovy’s documentation, some bug fixes or new features?&lt;/p></description><content:encoded>
<![CDATA[<p>Here’s the end of a pretty Groovy year coming, giving room for an even groovier one!</p>
<p>I hope you all enjoyed the holidays, and that you’re ready to pour in some more drops of Groovy into your software mixes!</p>
<p>Don’t forget to continue <a href="https://github.com/groovy/groovy-core">starring the Groovy project on Github</a>, if you haven’t done so, so we try to reach 1000 in the new year!</p>
<p>What are going to be your new year’s resolutions? Perhaps some contributions to Groovy’s documentation, some bug fixes or new features?</p>
<p>In the meantime, happy reading, here are a few news bits for your consumption!</p>
<p>And let me wish you a very Groovy year!</p>
<h2 id="articles">Articles</h2>
<ul>
<li><a href="http://arzur.net/octopress/blog/2014/12/28/metrics-plus-grails-equals-awesomeness/">Metrics + Grails = Awesomeness</a> by Erwan Arzur</li>
<li>Experiments (in French) with <a href="http://arzur.net/octopress/blog/2014/12/28/amusons-nous-avec-les-transactions-et-grails-dot-dot-dot/">Grails and transactions</a> by Erwan Arzur</li>
<li>A <a href="http://moduslaborandi.net/groovy-101-groovycalc-with-tests-and-travis-ci-integration/">Groovy 101</a> article also explaining how to use Groovy integration with Travis-CI</li>
<li><a href="http://davydotcom.com/blog/2014-12-21-spring-boot-with-the-asset-pipeline">Spring Boot with the Gradle Asset Pipeline</a> by David Estes</li>
<li>The <a href="http://www.intelligrape.com/blog/grails-stats/">Grails stats command gives useful insight</a> in the structure of your Grails projects</li>
<li>Abhinav Anand covers <a href="http://www.oodlestechnologies.com/blogs/tokenize%28%29-and-split%28%29-in-groovy">Groovy&rsquo;s tokenize() and split() methods</a></li>
<li><a href="http://www.oodlestechnologies.com/blogs/Nesting-custom-tags-in-grails">Nesting custom tags in Grails</a> by Abhinav Anand</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li><a href="http://www.bmeweb.it/category/groovy-weekly/">Groovy Weekly in Italian</a></li>
<li>GR8Conf US launched its <a href="http://gr8conf.us/#/">new website</a></li>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="http://grydeske.net/news/show/76">Grails Diary</a> week 52</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li><a href="https://github.com/uehaj/groovyz">Type classes in Groovy</a></li>
<li>Paul King shares an example of a simple <a href="http://groovyconsole.appspot.com/script/5646392177459200/">fluent English-like DSL using Groovy command chains</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Cédric Champeau is trying to solve a long standing feature request about <a href="https://twitter.com/CedricChampeau/status/548477288627847168">enforcing &lsquo;final&rsquo;</a></li>
<li>Cédric Champeau coins a catchy movie title: <a href="https://twitter.com/cedricchampeau/status/549629946889777152">Groovy Wars ep1: The Phantom MetaClass</a></li>
<li>A <a href="https://twitter.com/agoncal/status/548805556992241664">tribute to Groovy</a> by the Mike Flowers Pops celebrated by Antonio Goncalves</li>
<li>Craig Burke is looking forward to release his <a href="https://twitter.com/craigburke1/status/548546842892713984">document builder</a> by the end of this year</li>
<li>Dierk König highlights <a href="https://twitter.com/mittie/status/548508666358669312">Groovy&rsquo;s ability to take on the challenge to support many programming language concepts</a></li>
<li>Dean Del Ponte notices that <a href="https://twitter.com/ddelponte/status/547841332224339969">Grails 3 apps can be run like any Gradle app within IntelliJ IDEA Community edition</a></li>
<li><a href="https://twitter.com/gvmtool/status/549330992197304320">AsciidoctorJ is now available on GVM</a></li>
<li>Dierk König is <a href="https://twitter.com/mittie/status/549587163151872000">leveraging Groovy categories for an elegant integration with Frege</a>&rsquo;s data structures</li>
<li><a href="https://twitter.com/marc0der/status/549619315683889153">What makes the Groovy community shine</a>, according to Marco Vermeulen? Its people are kind and helpful.</li>
<li>Marco Vermeulen wishes the <a href="https://twitter.com/marc0der/status/549613033031729152">Gradle task API had dependency injection to ease testing</a></li>
<li>Marco Vermeulen is finding <a href="https://twitter.com/marc0der/status/549539981069656064">Groovy productive for authoring Gradle plugins</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>Venkat Subramaniam to demonstrate how to <a href="https://twitter.com/GrailsConf/status/548396817033879552">apply Groovy closures for fun and productivity</a> at GraisConf India</li>
<li>Cédric Champeau is going to <a href="https://twitter.com/cedricchampeau/status/549478535216496640">speak about why using Groovy at the Poitou JUG</a></li>
<li>Cédric Champeau will be speaking about <a href="https://twitter.com/CedricChampeau/status/549871190245126144">Groovy on Android at the MCE 2015</a> conference for mobile developers</li>
<li>The <a href="https://twitter.com/greachconf/status/549507636719128579">Greach conference launched its Lanyrd</a> page</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>First release candidate of Groovy 2.4</title><link>https://glaforge.dev/posts/2014/12/24/first-release-candidate-of-groovy-2-4/</link><pubDate>Wed, 24 Dec 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/12/24/first-release-candidate-of-groovy-2-4/</guid><description>&lt;p>Hot on the heels of our 2.3.9 update, the Groovy team is happy to release the first release candidate of Groovy 2.4 as another Xmas present!&lt;/p>
&lt;p>This release candidate is our upcoming new major version of Groovy, including:&lt;/p>
&lt;ul>
&lt;li>official support for the Android development platform: you can now develop full Android applications in Groovy, dramatically reducing boilerplate code while keeping performance and memory consumption at the same level as Java apps&lt;/li>
&lt;li>performance optimizations: lots of improvements have been implemented in both statically compiled Groovy code and dynamic code. (example report of an illustrative &lt;a href="https://gist.github.com/melix/ea819c77c4b568660877">micro-benchmark&lt;/a>)&lt;/li>
&lt;li>optimized memory use: reworked some compiler internals to reduce memory consumption&lt;/li>
&lt;li>traits refinements: like the ability to tell that a trait can only be applied to a specific type hierarchy&lt;/li>
&lt;li>and as usual, lots of bugfixes&lt;/li>
&lt;/ul>
&lt;p>We’re planning on releasing the final Groovy 2.4 version in January, once we’re happy with the stability and feedback from the community.&lt;/p></description><content:encoded>
<![CDATA[<p>Hot on the heels of our 2.3.9 update, the Groovy team is happy to release the first release candidate of Groovy 2.4 as another Xmas present!</p>
<p>This release candidate is our upcoming new major version of Groovy, including:</p>
<ul>
<li>official support for the Android development platform: you can now develop full Android applications in Groovy, dramatically reducing boilerplate code while keeping performance and memory consumption at the same level as Java apps</li>
<li>performance optimizations: lots of improvements have been implemented in both statically compiled Groovy code and dynamic code. (example report of an illustrative <a href="https://gist.github.com/melix/ea819c77c4b568660877">micro-benchmark</a>)</li>
<li>optimized memory use: reworked some compiler internals to reduce memory consumption</li>
<li>traits refinements: like the ability to tell that a trait can only be applied to a specific type hierarchy</li>
<li>and as usual, lots of bugfixes</li>
</ul>
<p>We’re planning on releasing the final Groovy 2.4 version in January, once we’re happy with the stability and feedback from the community.</p>
<p>You can read the <a href="https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=20785">Groovy 2.4-rc-1 release notes</a> to learn more about the tickets closed, and head over to the <a href="http://beta.groovy-lang.org/download.html">download</a> section of the beta Groovy website to get the latest and latest bits on your computer! The documentation for this version can be found <a href="http://docs.groovy-lang.org/2.4.0-rc-1/html/documentation/">here</a>.</p>
<p>We need your help to test drive this release candidate! We would greatly appreciate if you check this version against your projects and report back any regression or blocker that you come across.</p>
<p>Thanks a lot for all your contributions and support!</p>
<p>Keep on groovy-ing, and groovy holidays!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #50</title><link>https://glaforge.dev/posts/2014/12/24/groovy-weekly-50/</link><pubDate>Wed, 24 Dec 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/12/24/groovy-weekly-50/</guid><description>&lt;p>Lots among us are celebrating Christmas this week, and Groovy Weekly is happy to wish you all a Merry Christmas! And there’s also an anniversary, as it’s been one year that Groovy Weekly was launched, with the first edition on December 24th (hence my excuse to publish this column on Wednesday 24th instead of on Tuesday as usual)!&lt;/p>
&lt;p>For Christmas, the Groovy team decided to release a &lt;a href="https://glaforge.dev/posts/2014/12/24/first-release-candidate-of-groovy-2-4/">first release candidate for Groovy 2.4&lt;/a>, as well as a Groovy 2.3.9 update.&lt;/p></description><content:encoded>
<![CDATA[<p>Lots among us are celebrating Christmas this week, and Groovy Weekly is happy to wish you all a Merry Christmas! And there’s also an anniversary, as it’s been one year that Groovy Weekly was launched, with the first edition on December 24th (hence my excuse to publish this column on Wednesday 24th instead of on Tuesday as usual)!</p>
<p>For Christmas, the Groovy team decided to release a <a href="https://glaforge.dev/posts/2014/12/24/first-release-candidate-of-groovy-2-4/">first release candidate for Groovy 2.4</a>, as well as a Groovy 2.3.9 update.</p>
<p>Did you see this little guerrilla marketing campaign by Guillaume Laforge to encourage you all to make Groovy trendy by <a href="https://twitter.com/glaforge/status/545504762456969216">starring Groovy on Github</a>?</p>
<p>Thanks for helping us spread the word!</p>
<p>And last news item of the week, <a href="http://www.bmeweb.it/settimanale-groovy-2014-49/">Groovy Weekly is being translated in Italian</a>!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://glaforge.dev/posts/2014/12/24/first-release-candidate-of-groovy-2-4/">Groovy 2.4-rc-1</a> released!</li>
<li><a href="http://groovy.329449.n5.nabble.com/ANN-Groovy-2-3-9-td5721985.html">Groovy 2.3.9</a> released</li>
<li><a href="https://github.com/javaConductor/gserv/wiki/gServ-Home">gServ</a>, a new micro-service inspired, container-less tool to deploy Groovy REST services</li>
<li><a href="https://twitter.com/pledbrook/status/545178973651894272">Lazybones Gradle plugin v1.2.3</a></li>
<li><a href="http://groovy.329449.n5.nabble.com/ANN-Groovy-VFS-1-0-Beta-2-td5721979.html">Groovy VFS 1.0 Beta 2</a></li>
<li><a href="https://twitter.com/tim_yates/status/547510301634338816">Groovy-Stream v0.9</a> with new repeat methods</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li><a href="https://www.voxxed.com/blog/2014/12/grails-domain-classes-special-presentation-requirements/">Grails domain classes and special presentation requirements</a> by Ted Vinke</li>
<li>A <a href="http://mnmlst-dvlpr.blogspot.de/2014/12/my-lightweight-release-process.html">lightweight release process to Maven Central with Gradle</a>, TravisCI, Bintray</li>
<li>An <a href="http://blog.soat.fr/2014/12/spock-tester-autrement/">introduction to Spock</a> in French</li>
<li>MrHaki&rsquo;s Gradle Goodness: <a href="http://mrhaki.blogspot.fr/2014/12/gradle-goodness-rename-ant-task-names.html">rename Ant task names when importing Ant build files</a></li>
<li>Iván López&rsquo; <a href="http://www.kaleidos.net/blog/759/ggx-my-talk-and-general-impressions/">impressions of the Groovy Grails eXchange conference</a></li>
<li><a href="http://www.intelligrape.com/blog/groovy-annotations-for-logging/">Groovy annotations for logging</a> by Neetesh Bhardwaj</li>
<li><a href="http://www.intelligrape.com/blog/grails-performance-optimization-unique-constraint/">Grails performance optimization with the unique constraint</a> by Deepak Kumar Mittal</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li><a href="https://parleys.com/play/5471dd16e4b0e15e672384e7/chapter0/about">Groovy in the light of Java 8</a> by Guillaume Laforge at Devoxx 2014 (Parleys subscription required)</li>
</ul>
<h2 id="screencasts">Screencasts</h2>
<ul>
<li>A <a href="http://www.oreilly.com/pub/e/3286">Gradle for Android webcast</a> by Ken Kousen on O&rsquo;Reilly</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Julien Viet announces a <a href="https://groups.google.com/forum/#!msg/vertx/Pr8QejfMIj0/YGQ_QRQyK2cJ">preview of Vert.x 3</a> (Groovy language support included)</li>
<li>The <a href="http://www.bmeweb.it/settimanale-groovy-2014-49/">Groovy Weekly now in Italian</a>, thanks to Giuliano Lo Iacono!</li>
<li><a href="http://grydeske.net/news/show/75">Grails Diary week 51</a> by Jacob Aae Mikkelsen</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>The new repeat method in Groovy-stream helps generate the <a href="https://twitter.com/tim_yates/status/547517168708222976">fizz-buzz sequence without the need of conditionals</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li><a href="https://twitter.com/glaforge/status/545504762456969216">Star the Groovy project on Github</a> asks Guillaume Laforge!</li>
<li>Jeff Brown calls for <a href="https://twitter.com/jeffscottbrown/status/547516371412975617">help to test drive the Groovy 2.4 release candidate</a>!</li>
<li>Jörg Prante finds Groovy so much fun that he <a href="https://twitter.com/xbib/status/546425480657010690">embeds Ratpack in an ElasticSearch plugin</a></li>
<li>New documentation on <a href="https://twitter.com/ratpackweb/status/541908183212490752">how to use Jackson in Ratpack</a></li>
<li>The Grain static site generator releases a <a href="https://twitter.com/grainframework/status/545167185669545984">company website theme with a blog</a> included</li>
<li>Chris Earle reminds us that <a href="https://twitter.com/pickypg/status/545664315026845698">ElasticSearch&rsquo;s Groovy client</a> has been updated with full compatibility for ElasticSearch 1.4.2</li>
<li>Eugene Kamenev enjoys the <a href="https://twitter.com/eugenekamenev/status/545657795404525568">greater Groovy support in Android Studio</a></li>
<li>Greg Williams believes <a href="https://twitter.com/greg2020/status/545611812087943168">Groovy will become the Swift for Android</a></li>
<li>Peter Ledbrook added <a href="https://twitter.com/pledbrook/status/545881211047268352">documentation on the config command of Lazybones</a></li>
<li><a href="https://twitter.com/gvmtool/status/545978461509394432">Lazybones 0.8</a> available on GVM</li>
<li><a href="https://twitter.com/gvmtool/status/545960385237778432">Groovy 2.3.9</a> available on GVM</li>
<li>Iván López is having <a href="https://twitter.com/ilopmar/status/545238870485639169">fun with Groovy on Android</a></li>
<li><a href="https://twitter.com/grooscript/status/546274756484673536">GrooScript added Continous Integration</a> thanks to SnapCI</li>
<li>Cédric Champeau found a comic strip with a <a href="https://twitter.com/CedricChampeau/status/547513924157853697">hand G sign for top geekery</a>&hellip; and with a Groovy t-shirt!</li>
<li>The <a href="https://twitter.com/CedricChampeau/status/547503135011643394">JFrog Bintray team&rsquo;s been helping the Groovy team with deployment and release issues</a>, thanks guys!</li>
<li><a href="https://twitter.com/gvmtool/status/547502562791161860">Groovy 2.4-rc-1</a> available on GVM</li>
<li><a href="https://twitter.com/chriswhocodes/status/547403796754993153?cn=ZmF2b3JpdGVfbWVudGlvbmVkX3VzZXI%3D&amp;refsrc=email">JITWatch now has Groovy support</a> announced Chris Newland</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>The <a href="https://twitter.com/greachconf/status/546968408009371649">Greach call for papers has been extended to January 18th</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #49</title><link>https://glaforge.dev/posts/2014/12/16/groovy-weekly-49/</link><pubDate>Tue, 16 Dec 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/12/16/groovy-weekly-49/</guid><description>&lt;p>This has been a super busy week, in particular with the 7th edition of the Groovy Grails eXchange conference in London. You’ll find lots of slides and videos already published online, so you won’t miss a beat! For instance don’t miss this talk from Shuichisan how &lt;a href="https://www.skillsmatter.com/skillscasts/6107-developing-api-platform-in-groovy-at-ratuken">Groovy is used by Japan’s Rakuten mobile backend as a service platform at scale&lt;/a>!&lt;/p>
&lt;p>A special highlight in the articles section to Cédric’s article on &lt;a href="http://melix.github.io/blog/2014/12/10-things-static-cant-do.html">10 things your static language can’t do&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>This has been a super busy week, in particular with the 7th edition of the Groovy Grails eXchange conference in London. You’ll find lots of slides and videos already published online, so you won’t miss a beat! For instance don’t miss this talk from Shuichisan how <a href="https://www.skillsmatter.com/skillscasts/6107-developing-api-platform-in-groovy-at-ratuken">Groovy is used by Japan’s Rakuten mobile backend as a service platform at scale</a>!</p>
<p>A special highlight in the articles section to Cédric’s article on <a href="http://melix.github.io/blog/2014/12/10-things-static-cant-do.html">10 things your static language can’t do</a>.</p>
<h2 id="releases">Releases</h2>
<ul>
<li>A new version of the <a href="https://twitter.com/marioggar/status/544814985776734208">Grooid template for Groovy on Android development</a></li>
<li><a href="https://spring.io/blog/2014/12/11/spring-boot-1-2-0-released">Spring Boot 1.2</a> released</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Cédric Champeau lists <a href="http://melix.github.io/blog/2014/12/10-things-static-cant-do.html">10 things your static language can&rsquo;t do</a></li>
<li><a href="http://matthurne.com/blog/2014/using-groovy-extension-modules-with-gradle-shadow.html">Using Groovy extension modules with the Gradle Shadow plugin</a> by Matt Hurne</li>
<li><a href="http://www.objectpartners.com/2014/12/11/copying-multiple-directories-in-a-single-gradle-task-with-up-to-date-checking/">Copying multiple directories in a single Gradle task with up-to-date checking</a> by Craig Atkinson</li>
</ul>
<h2 id="groovy-grails-exchange-interviews">Groovy Grails eXchange Interviews</h2>
<ul>
<li><a href="http://blog.skillsmatter.com/2014/12/15/while-its-compiling-skills-matter-interviews-graeme-rocher/">Graeme Rocher interviewed about Grails</a></li>
<li><a href="http://blog.skillsmatter.com/2014/12/12/while-its-compiling-skills-matter-interviews-guillaume-laforge/">Guillaume Laforge interviewed about Groovy</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Paul King discusses the <a href="http://fr.slideshare.net/paulk_asert/awesome-groovy">good parts of Groovy</a> at YOW! 2014</li>
<li>All the <a href="https://www.skillsmatter.com/conferences/1957-groovy-grails-exchange-2014#skillscasts">presentations of the Groovy Grails eXchange 2014</a> conference are being put online on the conference website (you need to be logged-in to view the videos)</li>
<li>Day one
<ul>
<li>Guillaume Laforge&rsquo;s <a href="https://skillsmatter.com/skillscasts/6047-what-s-up-in-the-groovy-world">Groovy update keynote</a></li>
<li>Vladimír Oraný presented <a href="https://www.skillsmatter.com/skillscasts/6075-feed-your-grails-karma">&ldquo;Feed you Grails karma&rdquo;</a></li>
<li>Peter Ledbrook presented <a href="https://www.skillsmatter.com/skillscasts/5485-groovy-for-java-developers">“Groovy for Java developers”</a></li>
<li>Shuichi Suzuki spoke about &ldquo;<a href="https://www.skillsmatter.com/skillscasts/6107-developing-api-platform-in-groovy-at-ratuken">Developing API Platform in Groovy at Ratuken</a>&rdquo;</li>
<li>Alvaro Sanchez-Mariscal on &ldquo;<a href="https://www.skillsmatter.com/skillscasts/6058-stateless-authentication-for-microservices">Stateless authentication for microservices</a>&rdquo;</li>
<li>Russel Winder on &ldquo;<a href="https://www.skillsmatter.com/skillscasts/6081-spocktacular-testing">Spocktacular testing</a>&rdquo;</li>
<li>Jeff Brown on &ldquo;<a href="https://www.skillsmatter.com/skillscasts/6062-groovy-ast-transformations-and-type-checking-extensions">Groovy AST Transformations And Type Checking Extensions</a>&rdquo;</li>
<li>Schalk Cronjé on “<a href="https://www.skillsmatter.com/skillscasts/6049-groovy-vfs">Groovy VFS</a>”</li>
<li>Guillaume Laforge on &ldquo;<a href="https://www.skillsmatter.com/skillscasts/6072-behind-the-scenes-the-new-groovy-website">Behind the scenes of the new Groovy website and documentation</a>&rdquo;</li>
<li>Markus Schlichting on &ldquo;<a href="https://www.skillsmatter.com/skillscasts/6084-gradle-harder-better-stronger-faster">Gradle: harder, better, stronger, faster</a>&rdquo;</li>
</ul>
</li>
<li>Day two
<ul>
<li>Graeme Rocher&rsquo;s <a href="https://www.skillsmatter.com/skillscasts/4958-keynote-grails-three-point-zero-preview">Grails 3</a> keynote</li>
<li>Guillaume Laforge on “<a href="https://skillsmatter.com/skillscasts/6073-groovy-and-android-a-winning-pair">Groovy on Android</a>” presentation</li>
<li>Iván López on “<a href="https://skillsmatter.com/skillscasts/6074-grails-and-the-real-time-world#video">Grails and the real-time world</a>”</li>
<li>Marcin Erdmann on “<a href="https://skillsmatter.com/skillscasts/6064-running-an-open-source-project">Running an Open Source project</a>”</li>
<li>Jeff Brown on “<a href="https://www.skillsmatter.com/skillscasts/6063-restful-grails-2">Restful Grails 2</a>”</li>
<li>Marco Vermeulen on &ldquo;<a href="https://www.skillsmatter.com/skillscasts/6050-micro-service-architecture-with-spring-boot-and-groovy">Micro Service Architecture with Spring Boot and Groovy</a>&rdquo;</li>
<li>David Dawson on “<a href="https://www.skillsmatter.com/skillscasts/6044-forces-on-groovy-architecture">Forces on Groovy architecture</a>”</li>
<li>Guillaume Laforge on “<a href="https://www.skillsmatter.com/skillscasts/4957-groovy-with-style">Groovy with Style</a>”</li>
</ul>
</li>
<li>Slides from the Groovy Grails eXchange 2014 conference:
<ul>
<li>Guillaume Laforge published his slides from the “<a href="https://speakerdeck.com/glaforge/groovy-in-2014-and-beyond-groovy-grails-exchange-2014">Groovy update</a>” talk</li>
<li>Guillaume Laforge published the slides of the &ldquo;<a href="https://speakerdeck.com/glaforge/behind-the-scenes-of-the-new-groovy-website-and-documentation">Behind the Groovy website and documentation</a>&rdquo; presentation</li>
<li>Guillaume Laforge&rsquo;s “<a href="https://speakerdeck.com/glaforge/groovy-on-android-groovy-grails-exchange-2014">Groovy on Android</a>” slides</li>
<li>Guillaume Laforge’s “<a href="https://speakerdeck.com/glaforge/groovy-with-style-groovy-grails-exchange-2014">Groovy with style</a>” presentation</li>
<li>Russel Winder published the slides of his <a href="https://twitter.com/russel_winder/status/543038550631915520">&ldquo;Spocktacular testing&rdquo;</a> talk</li>
<li>Vladimír Oraný published the slides of his &ldquo;<a href="http://fr.slideshare.net/vladimirorany/feed-your-grails-karma-ggx-2014">Feed your Grails karma</a>&rdquo; talk</li>
<li>Slides of &ldquo;<a href="http://marcovermeulen.github.io/spring-boot-groovy-talk/#/">Micro-service architecture with Spring Boot and Groovy</a>&rdquo; by Marco Vermeulen</li>
</ul>
</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>The <a href="http://gr8conf.eu/#/">new GR8Conf Europe website</a> is launched!</li>
<li>You can <a href="https://youtrack.jetbrains.com/issue/IDEA-130198">vote for solving this regression on the support of IntelliJ IDEA for standard Groovy extension methods</a></li>
<li>Jacob Aae Mikkelsen’s <a href="http://grydeske.net/news/show/74">Grails Diary</a> week 50</li>
<li>This <a href="http://groovymn.tumblr.com/post/104760987832/this-month-in-gum">month in the Groovy Users of Minnesota</a></li>
</ul>
<h2 id="mailing-lists">Mailing-lists</h2>
<ul>
<li>An ongoing mailing-list thread is wondering <a href="http://groovy.329449.n5.nabble.com/The-single-USP-for-Groovy-td5721782.html">what is the unique selling point of Groovy</a>. What&rsquo;s yours?</li>
<li>Another ongoing thread on the Groovy lists is a discussion on <a href="http://groovy.329449.n5.nabble.com/Groovy-Value-Proposition-Compared-with-Java-8-td5721855.html">what is Groovy&rsquo;s value proposition compared with Java 8</a>. What&rsquo;s your take on that?</li>
<li>Alain Stalder shares a <a href="http://groovy.329449.n5.nabble.com/quot-Grey-quot-Sets-of-Groovy-Scripts-A-Case-Story-with-Groovy-and-Grengine-td5721936.html">case story with Groovy and his Grengine Groovy-powered scripting solution</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>An example of a <a href="http://sharing.beakernotebook.com/gist/anonymous/3fcbb2e8c0d67079d313">Beaker data scientist notebook using Groovy</a></li>
<li>Vladimír Oraný pushed online his <a href="https://github.com/musketyr/earls-list">demo application of his &ldquo;Feed Your Grails Karma&rdquo;</a> Groovy Grails eXchange talk</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Cédric Champeau thinks <a href="https://twitter.com/cedricchampeau/status/542996207854190593">Groovy&rsquo;s uniqueness is its runtime and compile-time capabilities</a></li>
<li>Craig Burke is making progress on his <a href="https://twitter.com/craigburke1/status/543064329533521921">Groovy document builder DSL</a></li>
<li>Luis Arias congratulates <a href="https://twitter.com/realkaaloo/status/543332004050530305">Guillaume Laforge for his 11+ years on the Groovy</a> project! How time flies…</li>
<li>Mario García published a <a href="https://twitter.com/marioggar/status/543358372477960192">Lazybones template for getting started with Groovy on Android development</a></li>
<li>During Graeme Rocher&rsquo;s keynote at Groovy Grails eXchange, Marcin Erdmann notes that <a href="https://twitter.com/marcinerdmann/status/543343346081890304">Grails 3 will use Geb for functional tests by default</a></li>
<li>Schalk Cronjé draws the <a href="https://twitter.com/ysb33r/status/542988039606521856">main points of Guillaume Laforge&rsquo;s Groovy update</a> presentation</li>
<li><a href="https://twitter.com/gvmtool/status/542985840545529856">Spring Boot 1.1.10</a> available on GVM</li>
<li><a href="https://twitter.com/gvmtool/status/542971505278459904">Spring Boot 1.2</a> available on GVM</li>
<li>Russel Winder notes <a href="https://twitter.com/russel_winder/status/542984966058311680">Groovy Macros will be re-scheduled to Groovy 2.5</a> to gather further feedback and real use-cases</li>
<li>Danny Hyun did a quick photoshop montage of <a href="https://twitter.com/lspacewalker/status/542340712134868992">Guillaume Laforge as the Fresh Prince of Bel-Air</a>, as Guillaume&rsquo;s transliteration name in English would be Will Smith!</li>
<li>Tomas Lin also reinterprets the <a href="https://twitter.com/tomaslin/status/542348040552448000">Fresh Prince of Bel Air family as the Groovy family</a></li>
<li>Sean Gilligan shows off a <a href="https://twitter.com/msgilligan/status/542606254405713920">screenshot of Groovy running on iOS</a></li>
<li>A nice list of useful and <a href="https://twitter.com/macg33zr/status/543046348153847808">interesting Gradle plugins to investigate</a> and play with</li>
<li>Brian Johnsen believes the <a href="https://twitter.com/brianjohnsendk/status/543061487741595649">Groovy core team have taken executable documentation to a new level</a></li>
<li>Angel Ruiz believes <a href="https://twitter.com/aruizca/status/543188269605732352">Groovy&rsquo;s executable documentation is how tech documentation should be done</a> these days</li>
<li>In <a href="https://twitter.com/ratpackweb/status/543187563947651072">Ratpack 0.9.12, a type can implement Renderable</a> to make it renderable</li>
<li>Robert Fletcher notes that <a href="https://twitter.com/rfletcherEW/status/543108794150944768">Groovy method closures can be coerced to Guava functions</a>, or actually any functional interface where Java 8 lambdas would be expected</li>
<li>Russel Winder remarks that <a href="https://twitter.com/russel_winder/status/543347767096410113">Grails 3 will be able to both be a serious full stack web application framework and a lightweight microframework</a></li>
<li>Phil Webb counted 194 people who <a href="https://twitter.com/phillip_webb/status/543271226542522368">voted for a Gradle &ldquo;provided&rdquo; scope</a></li>
<li>&ldquo;<a href="https://twitter.com/russel_winder/status/543356824947556353">The Java used on Android is out of the stone age</a>&rdquo;, said Guillaume Laforge during his Groovy on Android talk, and Russel Winder thinks it&rsquo;s worse than that!</li>
<li>Russel Winder believes <a href="https://twitter.com/russel_winder/status/543357338816888832">Android needs Java 8, or developers can just use Groovy, and that&rsquo;s what the New York Times decided to do</a> for its Android application</li>
<li>Another great feature of <a href="https://twitter.com/mgdelacroix/status/543344032475533312">Grails 3 is that it&rsquo;s coming integrated with tools that projects usually need: Gradle, Spock, Geb</a></li>
<li>Mario García is working on a new <a href="https://twitter.com/marioggar/status/543394656726556672">Lazybone template for authoring AST transformations for Groovy on Android</a></li>
<li>A <a href="https://twitter.com/lhotari/status/543488286229286912">picture of the Grails team and the Groovy lead</a> at Groovy Grails eXchange 2014</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="https://twitter.com/naresha_k/status/542982488658677760">Venkat Subramaniam will be speaking at GrailsConf India</a> on pleasure and perils of dynamic ecosystem</li>
<li>Read about the <a href="http://grailsconf.in/sessions">sessions of GrailsConf India</a></li>
<li>The <a href="http://www.meetup.com/Gr8Ladies/">GR8Ladies have created a Meetup page</a> to get the latest news, upcoming event announcements, etc</li>
<li>You can watch the <a href="https://skillsmatter.com/conferences/1957-groovy-grails-exchange-2014#photos">pictures of Groovy Grails eXchange</a> online</li>
<li><a href="https://twitter.com/gr8confus/status/544522716897083392">GR8Conf US 2015</a> will take place July 29th-31st</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #48</title><link>https://glaforge.dev/posts/2014/12/09/groovy-weekly-48/</link><pubDate>Tue, 09 Dec 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/12/09/groovy-weekly-48/</guid><description>&lt;p>Almost a year since I started the Groovy Weekly newsletter. And perhaps for the first anniversary, in a handful of weeks, we’ll have reached 2000 news items shared with you all in this column!&lt;/p>
&lt;p>This week is a London-ian one, as lots of the Groovy ecosystem inhabitants are migrating to the UK to celebrate the Groovy Grails eXchange conference. I’m looking forward to seeing some of you this week there. It also means that next week, you’ll likely see many links to slides and videos of the conference.&lt;/p></description><content:encoded>
<![CDATA[<p>Almost a year since I started the Groovy Weekly newsletter. And perhaps for the first anniversary, in a handful of weeks, we’ll have reached 2000 news items shared with you all in this column!</p>
<p>This week is a London-ian one, as lots of the Groovy ecosystem inhabitants are migrating to the UK to celebrate the Groovy Grails eXchange conference. I’m looking forward to seeing some of you this week there. It also means that next week, you’ll likely see many links to slides and videos of the conference.</p>
<p>And congrats to the Griffon team for a nice Griffon 2.1 release!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://groovy.329449.n5.nabble.com/ANN-Griffon-2-1-0-released-td5721850.html">Griffon 2.1.0</a> released</li>
<li><a href="https://twitter.com/grooscript/status/540115721611927552">GrooScript 0.6.3</a> released, equivalent of the first release candidate for 1.0</li>
<li>Spring Tool Suite and <a href="https://spring.io/blog/2014/12/02/spring-tool-suite-and-groovy-grails-tool-suite-3-6-3-released">Groovy/Grails Tool Suite 3.6.3</a> released</li>
<li>New versions of Craig Burke&rsquo;s <a href="https://twitter.com/craigburke1/status/539876227382005760">Grails and Angular.JS Gradle asset pipeline plugins</a></li>
<li>Groovy-based <a href="http://jenkins-ci.org/content/workflow-plugin-10">Jenkins Workflow plugin reaches 1.0</a></li>
<li><a href="https://twitter.com/grooscript/status/540597302704865280">GrooScript Gradle plugin v0.8</a> is out</li>
<li><a href="https://twitter.com/gradleplugins/status/541981425181155329">Asciidoctor Gradle plugin v1.5.2</a> released</li>
<li><a href="http://groovy.329449.n5.nabble.com/ANN-Groovy-VFS-1-0-Beta-1-td5721839.html">Groovy VFS 1.0 beta 1</a> released</li>
<li>The <a href="http://groovy.329449.n5.nabble.com/Windows-Installer-for-Groovy-2-3-8-td5721841.html">Groovy Windows installer for Groovy 2.3.8</a> is available</li>
<li><a href="https://twitter.com/andreyhihlovski/status/542045672623861760">Gretty 1.1.8</a> released</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>The <a href="http://beta.groovy-lang.org/style-guide.html">Groovy style guide</a> document migrates to the new documentation and website</li>
<li>Kyle Boon mixes <a href="http://kyleboon.org/blog/2014/08/14/ratpack-plus-docker-plus-gradle/">Ratpack, Docker and Gradle</a></li>
<li>Four steps to a <a href="http://blog.codenvy.com/cooking-custom-build-environments/">Docker-based build environment with CodeEnvye, showcasing Grails</a></li>
<li>MrHaki&rsquo;s Gradle Goodness: <a href="http://mrhaki.blogspot.fr/2014/12/gradle-goodness-skip-building-project.html">skip building project dependencies</a></li>
<li>Jochen Theodorou gives a deeper look at <a href="http://blackdragsview.blogspot.de/2014/12/a-deeper-look-at-default-methods.html">interface default methods conflicts and proxies</a></li>
<li>MrHaki&rsquo;s Gradle Goodness: <a href="http://mrhaki.blogspot.fr/2014/12/gradle-goodness-continue-build-even.html">continue build even with failed tasks</a></li>
<li><a href="http://hussain.io/2014/12/setting-up-sonarqube-for-grails-project/">Setting up SonarQube for Grails projects</a> by Hussain Fakhruddin</li>
<li>Parampreet Singh on <a href="http://www.intelligrape.com/blog/groovy-goodness-readwritelocks/">Groovy&rsquo;s @WithReadLock and @WithWriteLock transformations</a></li>
<li><a href="http://www.intelligrape.com/blog/restricting-concurrent-sessions-for-a-single-user-using-grails-and-spring-security/">Restricting concurrent sessions for a single user using Grails and Spring Security</a></li>
<li>More about the <a href="http://davydotcom.com/blog/2014-12-03-asset-pipeline-2-0">Asset-Pipeline Gradle plugin v2.0</a> by David Estes</li>
<li><a href="http://www.objectpartners.com/2014/12/03/case-insensitive-criteria-ordering-on-child-properties/">Case-insensitive criteria ordering on child properties</a> by Igor Shults</li>
<li><a href="http://www.groovy-code.com/2014/12/effective-grails-plugin-development.html">Effective Grails plugin development with in-place plugins</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Guillaume Laforge presented about <a href="https://twitter.com/virtualjug/status/539848790732472320">What makes Groovy groovy at the Virtual JUG</a></li>
<li>SpringOne2GX 2014 presentations on InfoQ
<ul>
<li><a href="http://www.infoq.com/presentations/android-groovy-jvm">Groovy on Android, a winning pair</a>, by Cédric Champeau</li>
<li><a href="http://www.infoq.com/presentations/grails-microservices-arch">Experiences using Grails in a microservice architecture</a>, by Jeff Beck</li>
<li><a href="http://www.infoq.com/presentations/groovy-java8-streams-api">How to get Groovy with Java 8</a>, by Peter Ledbrook</li>
</ul>
</li>
<li>Craig Atkinson delivered a GR8Ladies <a href="https://twitter.com/craigatk1/status/542041878276096000">workshop about TDD with Groovy and Spock</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li><a href="http://android-developers.blogspot.fr/2014/12/android-studio-10.html">Android Studio 1.0</a> out with its Gradle built-in support for building Android applications</li>
<li>Hans Dockter delivers the <a href="http://www.gradleware.com/newsletter/gradleware-newsletter-november-2014/">November Gradleware newsletter</a></li>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="http://grydeske.net/news/show/73">Grails Diary</a> week 49</li>
</ul>
<h2 id="screencasts">Screencasts</h2>
<ul>
<li>Bertrand Goetzmann released a screencast showing how to <a href="https://www.youtube.com/watch?v=yktftRpoeg4&amp;feature=youtu.be&amp;a">build a beautiful timeline with Linked Data, Spring Boot, Groovy, GroovySparql, and TimelineJS</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Following up a <a href="https://twitter.com/cedricchampeau/status/539802492000026624">conversation</a> on Twitter with Peter Ledbrook, Guillaume Laforge shares a gist showing <a href="https://gist.github.com/glaforge/1f481e0f0d043260aa9a">Groovy&rsquo;s List and Map coercion to types on assignments</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>The GrooScript project encourages you to <a href="https://twitter.com/grooscript/status/540169463271018496">try out GrooScript, to make a solid 1.0 release</a></li>
<li>Cédric Champeau shares the results of the Fibonacci <a href="https://gist.github.com/melix/ea819c77c4b568660877">micro-benchmark comparing various dynamic languages</a></li>
<li>Cédric Champeau and Jochen Theodorou continue looking into <a href="https://twitter.com/cedricchampeau/status/540117338784530433">optimizations for Groovy</a></li>
<li>The Warsaw Groovy user groups has some <a href="https://twitter.com/szimano/status/540070403268673536">cool Groovy stickers</a>!</li>
<li>The <a href="https://twitter.com/grooscript/status/539898692237795328">GrooScript project celebrates a first pull request</a>!</li>
<li>You can <a href="https://twitter.com/grooscript/status/540492118721298433">try online the GrooScript conversions of Groovy into JavaScript</a></li>
<li>New <a href="https://twitter.com/ratpackweb/status/541908183212490752">documentation on using Ratpack with the Jackson</a> library</li>
<li>Dan Allen believes <a href="https://twitter.com/mojavelinux/status/541554762575929344">Groovy succeeds in making us happy and efficient programmers</a></li>
<li>Sean Gilligan made <a href="https://twitter.com/msgilligan/status/539295258962436097">Groovy hello world work natively on Mac OS X</a> from Cédric Champeau&rsquo;s experiments with RoboVM</li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li>BloomHealth is <a href="https://twitter.com/shoemaker/status/540187482013573120">hiring Groovy + Angular.JS + Docker fans</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>Schalk Cronjé <a href="https://twitter.com/ysb33r/status/540415402246172672">reminds us the Call for Papers for Greach and GR8Conf are still open</a></li>
<li>You can <a href="http://groovy.329449.n5.nabble.com/Ann-Greach-You-can-buy-your-tickets-td5721819.html">buy your ticket for the Greach conference at an early bird price</a> till January 15th</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #47</title><link>https://glaforge.dev/posts/2014/12/02/groovy-weekly-47/</link><pubDate>Tue, 02 Dec 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/12/02/groovy-weekly-47/</guid><description>&lt;p>Today I highly recommend you read about the nice &lt;a href="http://www.elasticsearch.org/blog/making-elasticsearch-groovy-er/">Groovy dedicated support for ElasticSearch&lt;/a>! We also have a few interesting dot releases this week. And there’s the scoop that there’s a Ratpack book in the works!&lt;/p>
&lt;p>Don’t forget that the Call for Papers for GR8Conf Europe / US and Greach are still open, if you want to speak about your favorite Groovy technologies and experiments!&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://glaforge.dev/posts/2014/11/26/groovy-2-4-0-beta-4/">Groovy 2.4.0-beta-4&lt;/a> released&lt;/li>
&lt;li>&lt;a href="https://glaforge.dev/posts/2014/11/28/groovy-2-3-8-released-too/">Groovy 2.3.8&lt;/a> released&lt;/li>
&lt;li>&lt;a href="http://www.ratpack.io/versions/0.9.11">Ratpack 0.9.11&lt;/a> released&lt;/li>
&lt;li>&lt;a href="https://twitter.com/_yoav_/status/539551753742471168">Gradle Bintray plugin 1.0&lt;/a> released with Maven Central sync&lt;/li>
&lt;li>A much faster &lt;a href="https://twitter.com/davydotcom/status/539478433445994496">Asset Pipeline Gradle plugin with v2.0.4&lt;/a>&lt;/li>
&lt;li>First public version of an &lt;a href="https://twitter.com/gernotstarke/status/539441261531660289">HTML sanity checker Gradle plugin&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://plus.google.com/b/113675159854671799959/+AlBakerDev/posts/9tMes6wcaTX?cfem=1">GroovySparql 0.9&lt;/a> released&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>&amp;ldquo;&lt;a href="http://www.elasticsearch.org/blog/making-elasticsearch-groovy-er/">Making ElasticSearch Groovy-er&lt;/a>&amp;rdquo; with a dedicated Groovy client to interact with ElasticSearch&lt;/li>
&lt;li>Jochen &amp;ldquo;blackdrag&amp;rdquo; Theodorou experiments with Java&amp;rsquo;s processing API for a &lt;a href="http://blackdragsview.blogspot.fr/2014/11/a-joint-compiler-for-groovy-and-java.html">stub-less Groovy joint compiler&lt;/a>&lt;/li>
&lt;li>MrHaki&amp;rsquo;s Grails goodness: &lt;a href="http://mrhaki.blogspot.fr/2014/11/grails-goodness-create-new-application.html">create new application without wrapper&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://eshepelyuk.github.io/2014/11/26/-testing-jvm-javascript-jasmine-spock-nashorn.html">Testing JVM server-side JavaScript with Jasmine, Spock and Nashorn&lt;/a>, by Evgeny Shepelyuk&lt;/li>
&lt;li>Series of articles on &lt;a href="http://www.petrikainulainen.net/getting-started-with-gradle/">getting started with Gradle&lt;/a>&lt;/li>
&lt;li>In this interview, JetBrains developer Maxim Medvedev mentions that &lt;a href="http://blog.jetbrains.com/objc/2014/11/cooking-swift-an-interview-with-maxim-medvedev/">Groovy and Apple Swift are similar in terms of IDE support&lt;/a>, as both languages have type inference, closures, and higher order functions&lt;/li>
&lt;li>On &lt;a href="https://jkschneider.github.io/blog/2014/recursive-observables-with-rxjava.html">recursive observables with RxJava, and showing how Groovy can cut verbosity&lt;/a> down&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/madridgug/status/537944334008872960">Android development with Groovy&lt;/a>, by Mario García, at CodeMotion Spain 2014&lt;/li>
&lt;/ul>
&lt;h2 id="news">News&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://grydeske.net/news/show/72">Grails Diary week 48&lt;/a> by Jacob Aae Mikkelsen&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau created a &lt;a href="https://gist.github.com/melix/e4b63fd684e63713c162">Docker container with GVM and Groovy&lt;/a> installed&lt;/li>
&lt;li>A new &lt;a href="http://grooscript.org/demo/snapsvg.html">GrooScript demo with snap.svg&lt;/a>&lt;/li>
&lt;li>Using &lt;a href="https://gist.github.com/chiquitinxx/5192ff81d3b3351e3f1d">Firebase from your web pages with Groovy with GrooScript&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau teases for his Groovy Grails eXchange talk where he&amp;rsquo;ll explain how &lt;a href="https://twitter.com/cedricchampeau/status/538261967488974848">Groovy leverages Gradle, Asciidoctor, TeamCity for the new website&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/537882370066874369">Groovy 2.4 beta-4&lt;/a> available through GVM&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/cedricchampeau/status/537883662633959424">GVM announcement of the availability of Groovy 2.4 beta-4 was done from Groovy&amp;rsquo;s CI server&lt;/a> through GVM&amp;rsquo;s API&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/537874472888705024">Gradle 2.2.1&lt;/a> available on GVM&lt;/li>
&lt;li>Bertrand Goetzman spotted a &lt;a href="https://twitter.com/bgoetzmann/status/537363461832196097">Yeoman generator for Angular.JS + Ratpack&lt;/a> applications&lt;/li>
&lt;li>Tomas Lin suggests a &lt;a href="https://twitter.com/tomaslin/status/537436124189175809">Ratatouille inspired book cover for Dan Woods&amp;rsquo; upcoming Ratpack book&lt;/a>&lt;/li>
&lt;li>Eugene Kamenev is showing a screenshot of an &lt;a href="https://twitter.com/eugenekamenev/status/537664752621989889">Android Wear application written in Groovy&lt;/a>&lt;/li>
&lt;li>Roberto Guerra is having fun with &lt;a href="https://twitter.com/robertoguerra19/status/538048345529663489">Ratpack and Gulp.JS&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/538269325959106561">Groovy 2.3.8&lt;/a> has been released on GVM&lt;/li>
&lt;li>Dierk König is looking forward to using Tim Yates&amp;rsquo; &lt;a href="https://twitter.com/mittie/status/539056498073407488">groovy-stream library&lt;/a> more&lt;/li>
&lt;li>Jacob Aae Mikkelsen will be &lt;a href="https://twitter.com/JacobAae/status/538115269034774528">teaching web development with Groovy and Grails at the University of Southern Denmark&lt;/a>&lt;/li>
&lt;li>Rob Fletcher points at &lt;a href="https://twitter.com/rfletcherEW/status/538283579978317825">Gdub a tool that transparently uses gradle or gradlew when available&lt;/a>&lt;/li>
&lt;li>The Grain framework now features some &lt;a href="https://twitter.com/grainframework/status/539409491197173760">stylish bootstrap based templates&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="book">Book&lt;/h2>
&lt;ul>
&lt;li>Dan Woods will write a &lt;a href="https://twitter.com/danveloper/status/537277382571610112">book on Ratpack&lt;/a> for O&amp;rsquo;Reilly&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/greachconf/status/537548869429522433">Autentia becomes gold sponsor for the Greach&lt;/a> conference&lt;/li>
&lt;li>&lt;a href="https://twitter.com/greachconf/status/538273658494406656">Pronoid becomes gold sponsor of Greach&lt;/a>&lt;/li>
&lt;li>Remember that the &lt;a href="https://twitter.com/greachconf/status/538271091085418496">Call for Paper of the Greach conference is still open&lt;/a>!&lt;/li>
&lt;li>You can &lt;a href="https://twitter.com/greachconf/status/539356504244367360">buy tickets for the Greach&lt;/a> conference now!&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/gr8conf/status/539577626185052160">GR8Conf Europe and GR8Conf US Call for Paper&lt;/a> is still open&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>Today I highly recommend you read about the nice <a href="http://www.elasticsearch.org/blog/making-elasticsearch-groovy-er/">Groovy dedicated support for ElasticSearch</a>! We also have a few interesting dot releases this week. And there’s the scoop that there’s a Ratpack book in the works!</p>
<p>Don’t forget that the Call for Papers for GR8Conf Europe / US and Greach are still open, if you want to speak about your favorite Groovy technologies and experiments!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://glaforge.dev/posts/2014/11/26/groovy-2-4-0-beta-4/">Groovy 2.4.0-beta-4</a> released</li>
<li><a href="https://glaforge.dev/posts/2014/11/28/groovy-2-3-8-released-too/">Groovy 2.3.8</a> released</li>
<li><a href="http://www.ratpack.io/versions/0.9.11">Ratpack 0.9.11</a> released</li>
<li><a href="https://twitter.com/_yoav_/status/539551753742471168">Gradle Bintray plugin 1.0</a> released with Maven Central sync</li>
<li>A much faster <a href="https://twitter.com/davydotcom/status/539478433445994496">Asset Pipeline Gradle plugin with v2.0.4</a></li>
<li>First public version of an <a href="https://twitter.com/gernotstarke/status/539441261531660289">HTML sanity checker Gradle plugin</a></li>
<li><a href="https://plus.google.com/b/113675159854671799959/+AlBakerDev/posts/9tMes6wcaTX?cfem=1">GroovySparql 0.9</a> released</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>&ldquo;<a href="http://www.elasticsearch.org/blog/making-elasticsearch-groovy-er/">Making ElasticSearch Groovy-er</a>&rdquo; with a dedicated Groovy client to interact with ElasticSearch</li>
<li>Jochen &ldquo;blackdrag&rdquo; Theodorou experiments with Java&rsquo;s processing API for a <a href="http://blackdragsview.blogspot.fr/2014/11/a-joint-compiler-for-groovy-and-java.html">stub-less Groovy joint compiler</a></li>
<li>MrHaki&rsquo;s Grails goodness: <a href="http://mrhaki.blogspot.fr/2014/11/grails-goodness-create-new-application.html">create new application without wrapper</a></li>
<li><a href="http://eshepelyuk.github.io/2014/11/26/-testing-jvm-javascript-jasmine-spock-nashorn.html">Testing JVM server-side JavaScript with Jasmine, Spock and Nashorn</a>, by Evgeny Shepelyuk</li>
<li>Series of articles on <a href="http://www.petrikainulainen.net/getting-started-with-gradle/">getting started with Gradle</a></li>
<li>In this interview, JetBrains developer Maxim Medvedev mentions that <a href="http://blog.jetbrains.com/objc/2014/11/cooking-swift-an-interview-with-maxim-medvedev/">Groovy and Apple Swift are similar in terms of IDE support</a>, as both languages have type inference, closures, and higher order functions</li>
<li>On <a href="https://jkschneider.github.io/blog/2014/recursive-observables-with-rxjava.html">recursive observables with RxJava, and showing how Groovy can cut verbosity</a> down</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li><a href="https://twitter.com/madridgug/status/537944334008872960">Android development with Groovy</a>, by Mario García, at CodeMotion Spain 2014</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li><a href="http://grydeske.net/news/show/72">Grails Diary week 48</a> by Jacob Aae Mikkelsen</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Cédric Champeau created a <a href="https://gist.github.com/melix/e4b63fd684e63713c162">Docker container with GVM and Groovy</a> installed</li>
<li>A new <a href="http://grooscript.org/demo/snapsvg.html">GrooScript demo with snap.svg</a></li>
<li>Using <a href="https://gist.github.com/chiquitinxx/5192ff81d3b3351e3f1d">Firebase from your web pages with Groovy with GrooScript</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Cédric Champeau teases for his Groovy Grails eXchange talk where he&rsquo;ll explain how <a href="https://twitter.com/cedricchampeau/status/538261967488974848">Groovy leverages Gradle, Asciidoctor, TeamCity for the new website</a></li>
<li><a href="https://twitter.com/gvmtool/status/537882370066874369">Groovy 2.4 beta-4</a> available through GVM</li>
<li>The <a href="https://twitter.com/cedricchampeau/status/537883662633959424">GVM announcement of the availability of Groovy 2.4 beta-4 was done from Groovy&rsquo;s CI server</a> through GVM&rsquo;s API</li>
<li><a href="https://twitter.com/gvmtool/status/537874472888705024">Gradle 2.2.1</a> available on GVM</li>
<li>Bertrand Goetzman spotted a <a href="https://twitter.com/bgoetzmann/status/537363461832196097">Yeoman generator for Angular.JS + Ratpack</a> applications</li>
<li>Tomas Lin suggests a <a href="https://twitter.com/tomaslin/status/537436124189175809">Ratatouille inspired book cover for Dan Woods&rsquo; upcoming Ratpack book</a></li>
<li>Eugene Kamenev is showing a screenshot of an <a href="https://twitter.com/eugenekamenev/status/537664752621989889">Android Wear application written in Groovy</a></li>
<li>Roberto Guerra is having fun with <a href="https://twitter.com/robertoguerra19/status/538048345529663489">Ratpack and Gulp.JS</a></li>
<li><a href="https://twitter.com/gvmtool/status/538269325959106561">Groovy 2.3.8</a> has been released on GVM</li>
<li>Dierk König is looking forward to using Tim Yates&rsquo; <a href="https://twitter.com/mittie/status/539056498073407488">groovy-stream library</a> more</li>
<li>Jacob Aae Mikkelsen will be <a href="https://twitter.com/JacobAae/status/538115269034774528">teaching web development with Groovy and Grails at the University of Southern Denmark</a></li>
<li>Rob Fletcher points at <a href="https://twitter.com/rfletcherEW/status/538283579978317825">Gdub a tool that transparently uses gradle or gradlew when available</a></li>
<li>The Grain framework now features some <a href="https://twitter.com/grainframework/status/539409491197173760">stylish bootstrap based templates</a></li>
</ul>
<h2 id="book">Book</h2>
<ul>
<li>Dan Woods will write a <a href="https://twitter.com/danveloper/status/537277382571610112">book on Ratpack</a> for O&rsquo;Reilly</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="https://twitter.com/greachconf/status/537548869429522433">Autentia becomes gold sponsor for the Greach</a> conference</li>
<li><a href="https://twitter.com/greachconf/status/538273658494406656">Pronoid becomes gold sponsor of Greach</a></li>
<li>Remember that the <a href="https://twitter.com/greachconf/status/538271091085418496">Call for Paper of the Greach conference is still open</a>!</li>
<li>You can <a href="https://twitter.com/greachconf/status/539356504244367360">buy tickets for the Greach</a> conference now!</li>
<li>The <a href="https://twitter.com/gr8conf/status/539577626185052160">GR8Conf Europe and GR8Conf US Call for Paper</a> is still open</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.3.8 released too</title><link>https://glaforge.dev/posts/2014/11/28/groovy-2-3-8-released-too/</link><pubDate>Fri, 28 Nov 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/11/28/groovy-2-3-8-released-too/</guid><description>&lt;p>Hot on the heels of our &lt;a href="https://glaforge.dev/posts/2014/11/26/groovy-2-4-0-beta-4/">new beta for 2.4&lt;/a>, here&amp;rsquo;s a bug fix release with Groovy 2.3.8.&lt;/p>
&lt;p>You can have a look at the &lt;a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;version=20648">JIRA release&lt;/a> and you can &lt;a href="http://beta.groovy-lang.org/download.html">download Groovy 2.3.8&lt;/a> right away!&lt;/p>
&lt;p>Thanks all for your contributions!&lt;/p>
&lt;p>Keep on groovy&amp;rsquo;ing!&lt;/p></description><content:encoded>
<![CDATA[<p>Hot on the heels of our <a href="https://glaforge.dev/posts/2014/11/26/groovy-2-4-0-beta-4/">new beta for 2.4</a>, here&rsquo;s a bug fix release with Groovy 2.3.8.</p>
<p>You can have a look at the <a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=20648">JIRA release</a> and you can <a href="http://beta.groovy-lang.org/download.html">download Groovy 2.3.8</a> right away!</p>
<p>Thanks all for your contributions!</p>
<p>Keep on groovy&rsquo;ing!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.4.0-beta-4</title><link>https://glaforge.dev/posts/2014/11/26/groovy-2-4-0-beta-4/</link><pubDate>Wed, 26 Nov 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/11/26/groovy-2-4-0-beta-4/</guid><description>&lt;p>This is with great pleasure that we are announcing the release of Groovy 2.4.0-beta-4.&lt;/p>
&lt;p>The highlights for this release are:&lt;/p>
&lt;ul>
&lt;li>a rewritten JsonBuilder for improved performance in JSON generation&lt;/li>
&lt;li>a &lt;a href="http://docs.groovy-lang.org/2.4.0-beta-4/html/documentation/core-traits.html#_self_types">&lt;code>@SelfType&lt;/code>&lt;/a> annotation for traits&lt;/li>
&lt;li>a new &lt;a href="http://docs.groovy-lang.org/2.4.0-beta-4/html/gapi/groovy/text/StreamingTemplateEngine.html">variant of &lt;code>GStringTemplateEngine&lt;/code>&lt;/a> capable of handling strings larger than 64k&lt;/li>
&lt;li>improved support for overloaded setters&lt;/li>
&lt;li>lots of bugfixes (some of which are backported in the upcoming 2.3.8 release)&lt;/li>
&lt;li>a new naming convention for closures&lt;/li>
&lt;/ul>
&lt;p>The last point is important if your project somehow relies on the name of the closure classes as it is a potential breaking change. The reason for the new scheme is detailed &lt;a href="https://jira.codehaus.org/browse/GROOVY-5351">here&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>This is with great pleasure that we are announcing the release of Groovy 2.4.0-beta-4.</p>
<p>The highlights for this release are:</p>
<ul>
<li>a rewritten JsonBuilder for improved performance in JSON generation</li>
<li>a <a href="http://docs.groovy-lang.org/2.4.0-beta-4/html/documentation/core-traits.html#_self_types"><code>@SelfType</code></a> annotation for traits</li>
<li>a new <a href="http://docs.groovy-lang.org/2.4.0-beta-4/html/gapi/groovy/text/StreamingTemplateEngine.html">variant of <code>GStringTemplateEngine</code></a> capable of handling strings larger than 64k</li>
<li>improved support for overloaded setters</li>
<li>lots of bugfixes (some of which are backported in the upcoming 2.3.8 release)</li>
<li>a new naming convention for closures</li>
</ul>
<p>The last point is important if your project somehow relies on the name of the closure classes as it is a potential breaking change. The reason for the new scheme is detailed <a href="https://jira.codehaus.org/browse/GROOVY-5351">here</a>.</p>
<p>You can download Groovy in our download area:
<a href="http://beta.groovy-lang.org/download.html">http://beta.groovy-lang.org/download.html</a></p>
<p>You can consult the JIRA change log:
<a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=20612">http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=20612</a></p>
<p>Thanks for your feedback and contributions!</p>
<p>Note that we are likely to change the scope of the 2.4 to exclude macros. There’s still a lot of work and discussion to be done on that topic, making it incompatible with our target for a 2.4 release. This feature is likely to be delayed to Groovy 2.5.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #46</title><link>https://glaforge.dev/posts/2014/11/25/groovy-weekly-46/</link><pubDate>Tue, 25 Nov 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/11/25/groovy-weekly-46/</guid><description>&lt;p>My favorite read of the week is definitely Ken Kousen’s tale about Groovy’s &lt;a href="http://www.accelebrate.com/blog/call-pogo-name/">Plain Old Groovy Object&lt;/a>! Note the releases of the new Groovy Eclipse support and the Eclipse Maven batch compiler. Last but not least, for those of you heading to the &lt;a href="https://skillsmatter.com/conferences/1957-groovy-grails-exchange-2014">Groovy &amp;amp; Grails eXchange conference in London&lt;/a>, notice the &lt;a href="https://skillsmatter.com/conferences/1957-groovy-grails-exchange-2014#program">program&lt;/a> of the conference unfolding before your eyes!&lt;/p>
&lt;p>Let me also mention the &lt;a href="http://beakernotebook.com/index">Beaker notebook project&lt;/a>, which offers Groovy support for data scientists who want to explore their data sets. And the &lt;a href="https://twitter.com/virtualkenji/status/535280638580887553">Spoon.net&lt;/a> team which features Groovy support for its Windows containers.&lt;/p></description><content:encoded>
<![CDATA[<p>My favorite read of the week is definitely Ken Kousen’s tale about Groovy’s <a href="http://www.accelebrate.com/blog/call-pogo-name/">Plain Old Groovy Object</a>! Note the releases of the new Groovy Eclipse support and the Eclipse Maven batch compiler. Last but not least, for those of you heading to the <a href="https://skillsmatter.com/conferences/1957-groovy-grails-exchange-2014">Groovy &amp; Grails eXchange conference in London</a>, notice the <a href="https://skillsmatter.com/conferences/1957-groovy-grails-exchange-2014#program">program</a> of the conference unfolding before your eyes!</p>
<p>Let me also mention the <a href="http://beakernotebook.com/index">Beaker notebook project</a>, which offers Groovy support for data scientists who want to explore their data sets. And the <a href="https://twitter.com/virtualkenji/status/535280638580887553">Spoon.net</a> team which features Groovy support for its Windows containers.</p>
<p>Final word, don’t miss Graeme’s tweets pointing at the Grails 3 scaffolding plugin and code generation APIs!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://github.com/groovy/groovy-eclipse/wiki/Groovy-Eclipse-2.9.1-Release-Notes">Groovy Eclipse 2.9.1</a> released</li>
<li><a href="http://groovy.329449.n5.nabble.com/Groovy-Eclipse-Maven-Batch-comiler-2-3-7-release-candidate-available-for-testing-td5721719.html">Groovy Eclipse Maven Batch comiler 2.3.7</a> release candidate available for testing</li>
<li><a href="http://forums.gradle.org/gradle/topics/gradle-2-2-1-released">Gradle 2.2.1</a> released</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>An awesome tale about POGOs by Ken Kousen: &ldquo;<a href="http://www.accelebrate.com/blog/call-pogo-name/">That Which We Call A POGO, By Any Other Name</a>&rdquo;</li>
<li><a href="http://www.oodlestechnologies.com/blogs/How-to-use-Groovy-Traits">How to use Groovy traits</a> by Rohan Jain</li>
<li><a href="https://solidsoft.wordpress.com/2014/11/19/gradle-tricks-tracking-down-not-expected-transitive-dependencies/">Tracking down not expected transitive dependencies in multi-project Gradle build</a> by Marcin Zajączkowski</li>
<li>Craig Burke publishes the second part on <a href="http://www.craigburke.com/2014/11/24/angular-grails-template-2.html">advanced customization of the Angular.JS Grails template</a></li>
<li>MrHaki&rsquo;s Gradle goodness: <a href="http://mrhaki.blogspot.fr/2014/11/gradle-goodness-using-and-working-with.html">Using and working with Gradle version</a></li>
<li>MrHaki&rsquo;s Gradle goodness: <a href="http://mrhaki.blogspot.fr/2014/11/gradle-goodness-using-copyspec-with.html">Using CopySpeck with Tasks</a></li>
<li>How to use <a href="http://www.oodlestechnologies.com/blogs/How-to-use-custom-tags-in-grails-controller%2C-services-groovy-file">custom tags in Grails controllers and services</a> by Ankush Kocher</li>
<li>How to <a href="http://aruizca.com/how-to-render-json-properly-without-escaping-quotes-inside-a-gsp-script-tag/">render JSON properly inside a GSP script tag</a> by Angel Ruiz</li>
<li><a href="http://www.intelligrape.com/blog/2014/11/19/object-based-security-using-spring-security-acl-in-grails/">Object based security using Spring Security ACL in Grails</a> by Anil Agrawal</li>
<li>Ames Lorenzen shares an <a href="http://java.dzone.com/articles/example-using-grails-promises">example using Grails promises</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Cédric Champeau presented in French a quick <a href="https://speakerdeck.com/melix/human-talks-nayez-pas-peur-de-faire-du-groovy">introduction to Groovy and its powers</a>, and the slides are quite self-explanatory even if you don&rsquo;t speak French!</li>
<li>A quick <a href="http://fr.slideshare.net/Ciklum_Kyiv/groovy-on-android">intro and comparison between Groovy and Apple Swift</a> in the context of mobile development with Android, by Olexandr Leuschenko</li>
<li><a href="https://twitter.com/alsargent/status/536019492204068864">Scalable Big Data stream processing with Apache Storm and Groovy</a> by Eugene Dvorkin, at SpringOne2GX 2014</li>
</ul>
<h2 id="screencasts">Screencasts</h2>
<ul>
<li>Dan Woods is preparing a <a href="http://www.oreilly.com/pub/e/3275">webcast about Ratpack</a> for O&rsquo;Reilly</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="http://grydeske.net/news/show/71">Grails Diary</a> week 47</li>
<li>The <a href="http://beakernotebook.com/index">data scientist&rsquo;s laboratory, Beaker, offers support for Groovy</a> for working with large and complex datasets</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Guillaume Laforge mentions on twitter a <a href="https://twitter.com/glaforge/status/535518780185903104">telescope project using Groovy</a></li>
<li>Graeme Rocher worked on the <a href="https://twitter.com/graemerocher/status/537184433489866753">first cut of the new Grails 3 scaffolding plugin</a> with the new code generation APIs</li>
<li>Graeme Rocher shows <a href="https://twitter.com/graemerocher/status/537185286456115200">how to implement a codegen script for Grails 3</a></li>
<li>Craig Burke is working on a <a href="https://twitter.com/craigburke1/status/535452259363356673">Word and PDF Groovy builder</a></li>
<li>Luke Daley launches a rumor about <a href="https://twitter.com/ldaley/status/535499624682119170">Dan Woods working on a Ratpack book</a> for O&rsquo;Reilly</li>
<li>Rob Fletcher <a href="https://twitter.com/rfletcherew/status/535426936974368768">appreciates Groovy even more when forced to write Java</a> code</li>
<li>Andrés Almiray releases v0.1 of the <a href="https://twitter.com/gradleplugins/status/536270638898774017">Clirr Gradle plugin for checking binary compatibility between different API versions</a></li>
<li>Dan Woods tells anyone <a href="https://twitter.com/danveloper/status/534857082965880832">excited by the idea of a Java REPL that there&rsquo;s already one available, Groovy&rsquo;s one!</a></li>
<li>Peter Ledbrook says <a href="https://twitter.com/pledbrook/status/535094362645233665">Groovy Grails eXchange will feature a beginner track</a> on the first day and a full day hacking session on the second</li>
<li>Jacek Laskowski notes that the <a href="https://twitter.com/jaceklaskowski/status/536889293307146240">Gremlin terminal is a Groovy shell</a></li>
<li>Etienne Studer releases a <a href="https://twitter.com/etiennestuder/status/536696709112283136">Gradle plugin to help plugin authors with bundling and publishing of Gradle plugins</a></li>
<li><a href="https://twitter.com/gvmtool/status/536066336850735104">Spring Boot 1.2-rc-2</a> is available on GVM</li>
<li>Ryan Vanderwerf is looking forward <a href="https://twitter.com/RyanVanderwerf/status/535911468424638464">programming in Groovy on his Xmas Android smartwatch</a></li>
<li>Bruno Borges announces Groo<a href="https://twitter.com/brunoborges/status/535999133254184960">vy support for scripted Java FX on webservers</a></li>
<li>Erik Pragt is working on the <a href="https://twitter.com/epragt/status/536091467153162240">IntelliJ IDEA support for Asciidoctor and shows a screenshot of the Groovy documentation</a> as an example</li>
<li>Iván López spoke about <a href="https://twitter.com/ilopmar/status/536115353815105536">Groovy on Android to a packed room at the CodeMotion</a> conference</li>
<li>Burt Beckwith shares a tip for those who can&rsquo;t use Spock: <a href="https://twitter.com/burtbeckwith/status/536716841737916416">add a static import on GroovyAssert#shouldFail to JUnit 4 tests</a> to follow the same pattern as in JUnit 3</li>
<li>Spoon.net releases a <a href="https://twitter.com/virtualkenji/status/535280638580887553">Groovy image for its Windows container engine</a></li>
<li>Craig Burke says that <a href="https://twitter.com/craigburke1/status/535112280703516673">reading on functional programming in Java 8 makes him appreciate Groovy closures</a> even more</li>
</ul>
<h2 id="book">Book</h2>
<ul>
<li>Schalk Cronjé is looking for <a href="https://twitter.com/ysb33r/status/535574251114598400">feedback on smart techniques used within your Gradle plugins for contributions for a Gradle book</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="http://www.meetup.com/virtualJUG/events/218827084/?gj=rcs.a&amp;a=co2.a_grp&amp;rv=rcs.a">Guillaume Laforge will be speaking at the Virtual JUG</a></li>
<li>The <a href="https://twitter.com/Gr8Ladies/status/534886675424755712">December GR8 Ladies meetup</a> is sponsored by BloomHealth and AgileOrbit</li>
<li><a href="https://twitter.com/gr8ladiesmsp/status/535499595175182336">Object Partners becomes platinum sponsor of the GR8 Ladies</a> December GR8 workshop</li>
<li>The <a href="https://skillsmatter.com/conferences/1957-groovy-grails-exchange-2014#program">Groovy &amp; Grails eXchange conference agenda</a> is uncovering</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #45</title><link>https://glaforge.dev/posts/2014/11/18/groovy-weekly-45/</link><pubDate>Tue, 18 Nov 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/11/18/groovy-weekly-45/</guid><description>&lt;p>This week, coming back from the Devoxx conference in Belgium, I’ve gathered a bag of Groovy news for your consumption, for our usual Tuesday Groovy Weekly release!&lt;/p>
&lt;p>No particular big highlight today, but perhaps just a happy birthday to GrooScript, the Groovy to JavaScript transpiler, and also notice the opening of the Call for Paper for the GR8Conf conferences in Europe and in the United States.&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://forums.gradle.org/gradle/topics/gradle-2-2-1-rc-1-is-now-available-for-testing">Gradle 2.2.1-rc-1&lt;/a> released&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>Luke Daley explains &lt;a href="http://ldaley.com/post/102495950257/ratpacks-execution-model-in-practice">Ratpack&amp;rsquo;s execution model in practice&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://grysz.com/2014/11/14/custom-collections-in-groovy/">Custom collections in Groovy&lt;/a> by Marcin Gryszko&lt;/li>
&lt;li>MrHaki&amp;rsquo;s Gradle Goodness: &lt;a href="http://mrhaki.blogspot.fr/2014/11/gradle-goodness-check-task-dependencies.html">Check task dependencies with a dry run&lt;/a>&lt;/li>
&lt;li>Dan Tanner wrote about the &lt;a href="http://otherthanthink.blogspot.fr/2014/11/groovy-collect-vs-spread-dot-operator.html">differences between Groovy&amp;rsquo;s collect and spread-dot operator&lt;/a>&lt;/li>
&lt;li>Displaying &lt;a href="https://solidsoft.wordpress.com/2014/11/13/gradle-tricks-display-dependencies-for-all-subprojects-in-multi-project-build/">all dependencies for all subprojects in a multi-project Gradle build&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.objectpartners.com/2014/11/11/stringtemplates-in-groovy/">String templates in Groovy&lt;/a> by Mike Hostetler&lt;/li>
&lt;li>Craig Burke writes the first article of a series about the &lt;a href="http://www.craigburke.com/2014/11/17/angular-grails-template-1.html">Angular.JS Grails template&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>Devoxx special
&lt;ul>
&lt;li>Guillaume Laforge presented &amp;ldquo;&lt;a href="https://speakerdeck.com/glaforge/groovy-in-the-light-of-java-8-devoxx-2014">Groovy in the light of Java 8&lt;/a>&amp;rdquo;&lt;/li>
&lt;li>Guillaume Laforge presented &amp;ldquo;&lt;a href="https://speakerdeck.com/glaforge/groovy-in-2014-and-beyond-devoxx-2014">Groovy in 2014 and beyond&lt;/a>&amp;rdquo;&lt;/li>
&lt;li>Cédric Champeau presented &amp;ldquo;&lt;a href="https://speakerdeck.com/melix/groovy-and-android-a-winning-pair-1">Groovy and Android, the winning pair&lt;/a>&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Jérôme Louvel from the Restlet and APISpark projects mentions a &lt;a href="https://twitter.com/jlouvel/status/532699896600477696">PayPal API platform presentation showing Groovy being used for API composition&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="mailing-lists">Mailing-lists&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://groovy.329449.n5.nabble.com/New-version-of-groovy-eclipse-compiler-td5721689.html">New versions of the Groovy Eclipse compiler&lt;/a> are coming along&lt;/li>
&lt;/ul>
&lt;h2 id="news">News&lt;/h2>
&lt;ul>
&lt;li>An update of &lt;a href="https://twitter.com/groovyblogs/status/533146828275859456">GroovyBlogs with infinite loading of recent and popular blog posts&lt;/a>&lt;/li>
&lt;li>Andrés Almiray found this old gem in honor of Dick Wall from the Javaposse fame as the podcast comes to its end, showing &lt;a href="http://blog.cliffano.com/2009/03/02/lovin-the-groovy/">Dick loving the Groovy&lt;/a>&lt;/li>
&lt;li>Jacob Aae Mikkelsen&amp;rsquo;s &lt;a href="http://grydeske.net/news/show/70">Grails Diary week 46&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/grooscript/status/533687966951297026">GrooScript celebrates 2 years since the 0.1 release&lt;/a>!&lt;/li>
&lt;li>Lari Hotari is working on &lt;a href="https://twitter.com/lhotari/status/534082054527672320">Grails 3 / Spring Boot startup time optimisations&lt;/a>&lt;/li>
&lt;li>Patrick Double published a &lt;a href="https://twitter.com/DoubleCreekNE/status/532295990586658816">Grails plugin to improve Grails / SauceLabs Geb integration&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/grooscript/status/532619382703521792">GrooScript chat application deployed on Heroku&lt;/a> with Node.JS and Socket.IO&lt;/li>
&lt;li>Ratpack features a &lt;a href="https://twitter.com/ratpackweb/status/532631677411917826">URL builder&lt;/a> utility&lt;/li>
&lt;li>Hans Dockter showed &lt;a href="https://twitter.com/CedricChampeau/status/532841402733178880">declarative dependency replacement with Gradle&lt;/a> at Devoxx 2014&lt;/li>
&lt;li>Hans Dockter showed how to &lt;a href="https://twitter.com/CedricChampeau/status/532842212581978112">tell Gradle that you don&amp;rsquo;t want to depend on beta dependencies&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/534458011893633025">Gradle 2.2.1-rc-1&lt;/a> available on GVM&lt;/li>
&lt;li>Álvaro Sánchez sparks a discussion about &lt;a href="https://twitter.com/alvaro_sanchez/status/532477553576271872">Spock labels indentation&lt;/a> on twitter&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau is working on &lt;a href="https://gist.github.com/melix/6875e12077908129f400">AST tree matching&lt;/a>&lt;/li>
&lt;li>Vladimir Orany shares a snippet showing how to &lt;a href="https://gist.github.com/musketyr/611cacb57639dcaa6b94">enable DSL support in IntelliJ IDEA&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>The &lt;a href="http://us4.campaign-archive2.com/?u=ac7af4c02d6cec67fe3198a63&amp;amp;id=60cfa3d0b4&amp;amp;e=88c97e251c">Call for Paper for GR8Conf Europe and GR8Conf US is open&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>This week, coming back from the Devoxx conference in Belgium, I’ve gathered a bag of Groovy news for your consumption, for our usual Tuesday Groovy Weekly release!</p>
<p>No particular big highlight today, but perhaps just a happy birthday to GrooScript, the Groovy to JavaScript transpiler, and also notice the opening of the Call for Paper for the GR8Conf conferences in Europe and in the United States.</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://forums.gradle.org/gradle/topics/gradle-2-2-1-rc-1-is-now-available-for-testing">Gradle 2.2.1-rc-1</a> released</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Luke Daley explains <a href="http://ldaley.com/post/102495950257/ratpacks-execution-model-in-practice">Ratpack&rsquo;s execution model in practice</a></li>
<li><a href="http://grysz.com/2014/11/14/custom-collections-in-groovy/">Custom collections in Groovy</a> by Marcin Gryszko</li>
<li>MrHaki&rsquo;s Gradle Goodness: <a href="http://mrhaki.blogspot.fr/2014/11/gradle-goodness-check-task-dependencies.html">Check task dependencies with a dry run</a></li>
<li>Dan Tanner wrote about the <a href="http://otherthanthink.blogspot.fr/2014/11/groovy-collect-vs-spread-dot-operator.html">differences between Groovy&rsquo;s collect and spread-dot operator</a></li>
<li>Displaying <a href="https://solidsoft.wordpress.com/2014/11/13/gradle-tricks-display-dependencies-for-all-subprojects-in-multi-project-build/">all dependencies for all subprojects in a multi-project Gradle build</a></li>
<li><a href="http://www.objectpartners.com/2014/11/11/stringtemplates-in-groovy/">String templates in Groovy</a> by Mike Hostetler</li>
<li>Craig Burke writes the first article of a series about the <a href="http://www.craigburke.com/2014/11/17/angular-grails-template-1.html">Angular.JS Grails template</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Devoxx special
<ul>
<li>Guillaume Laforge presented &ldquo;<a href="https://speakerdeck.com/glaforge/groovy-in-the-light-of-java-8-devoxx-2014">Groovy in the light of Java 8</a>&rdquo;</li>
<li>Guillaume Laforge presented &ldquo;<a href="https://speakerdeck.com/glaforge/groovy-in-2014-and-beyond-devoxx-2014">Groovy in 2014 and beyond</a>&rdquo;</li>
<li>Cédric Champeau presented &ldquo;<a href="https://speakerdeck.com/melix/groovy-and-android-a-winning-pair-1">Groovy and Android, the winning pair</a>&rdquo;</li>
</ul>
</li>
<li>Jérôme Louvel from the Restlet and APISpark projects mentions a <a href="https://twitter.com/jlouvel/status/532699896600477696">PayPal API platform presentation showing Groovy being used for API composition</a></li>
</ul>
<h2 id="mailing-lists">Mailing-lists</h2>
<ul>
<li><a href="http://groovy.329449.n5.nabble.com/New-version-of-groovy-eclipse-compiler-td5721689.html">New versions of the Groovy Eclipse compiler</a> are coming along</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>An update of <a href="https://twitter.com/groovyblogs/status/533146828275859456">GroovyBlogs with infinite loading of recent and popular blog posts</a></li>
<li>Andrés Almiray found this old gem in honor of Dick Wall from the Javaposse fame as the podcast comes to its end, showing <a href="http://blog.cliffano.com/2009/03/02/lovin-the-groovy/">Dick loving the Groovy</a></li>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="http://grydeske.net/news/show/70">Grails Diary week 46</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li><a href="https://twitter.com/grooscript/status/533687966951297026">GrooScript celebrates 2 years since the 0.1 release</a>!</li>
<li>Lari Hotari is working on <a href="https://twitter.com/lhotari/status/534082054527672320">Grails 3 / Spring Boot startup time optimisations</a></li>
<li>Patrick Double published a <a href="https://twitter.com/DoubleCreekNE/status/532295990586658816">Grails plugin to improve Grails / SauceLabs Geb integration</a></li>
<li><a href="https://twitter.com/grooscript/status/532619382703521792">GrooScript chat application deployed on Heroku</a> with Node.JS and Socket.IO</li>
<li>Ratpack features a <a href="https://twitter.com/ratpackweb/status/532631677411917826">URL builder</a> utility</li>
<li>Hans Dockter showed <a href="https://twitter.com/CedricChampeau/status/532841402733178880">declarative dependency replacement with Gradle</a> at Devoxx 2014</li>
<li>Hans Dockter showed how to <a href="https://twitter.com/CedricChampeau/status/532842212581978112">tell Gradle that you don&rsquo;t want to depend on beta dependencies</a></li>
<li><a href="https://twitter.com/gvmtool/status/534458011893633025">Gradle 2.2.1-rc-1</a> available on GVM</li>
<li>Álvaro Sánchez sparks a discussion about <a href="https://twitter.com/alvaro_sanchez/status/532477553576271872">Spock labels indentation</a> on twitter</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Cédric Champeau is working on <a href="https://gist.github.com/melix/6875e12077908129f400">AST tree matching</a></li>
<li>Vladimir Orany shares a snippet showing how to <a href="https://gist.github.com/musketyr/611cacb57639dcaa6b94">enable DSL support in IntelliJ IDEA</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>The <a href="http://us4.campaign-archive2.com/?u=ac7af4c02d6cec67fe3198a63&amp;id=60cfa3d0b4&amp;e=88c97e251c">Call for Paper for GR8Conf Europe and GR8Conf US is open</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #44</title><link>https://glaforge.dev/posts/2014/11/11/groovy-weekly-44/</link><pubDate>Tue, 11 Nov 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/11/11/groovy-weekly-44/</guid><description>&lt;p>Today is the celebration of the veteran day / remembrance day in various countries throughout the world. So I’ll start with a big thank you to our ancestors who fought for our freedom!&lt;/p>
&lt;p>This week is also a busy week as a part of the Groovy core team is moving to Antwerpen in Belgium to speak about Groovy at the Devoxx conference.&lt;/p>
&lt;p>My picks of the week, among the news below are: the release of Gradle 2.2, the release of ElasticSearch 1.4 which now standardizes on Groovy for its default scripting language, and the announcement of the dates of the Greach conference in Spain and its call for paper which is open.&lt;/p></description><content:encoded>
<![CDATA[<p>Today is the celebration of the veteran day / remembrance day in various countries throughout the world. So I’ll start with a big thank you to our ancestors who fought for our freedom!</p>
<p>This week is also a busy week as a part of the Groovy core team is moving to Antwerpen in Belgium to speak about Groovy at the Devoxx conference.</p>
<p>My picks of the week, among the news below are: the release of Gradle 2.2, the release of ElasticSearch 1.4 which now standardizes on Groovy for its default scripting language, and the announcement of the dates of the Greach conference in Spain and its call for paper which is open.</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://www.elasticsearch.org/blog/elasticsearch-1-4-0-released/">ElasticSearch 1.4.0</a> released with Groovy as the default scripting language</li>
<li><a href="http://forums.gradle.org/gradle/topics/gradle-2-2-released">Gradle 2.2</a> released</li>
<li><a href="https://twitter.com/grailsframework/status/530268377080946689">GORM for Cassandra</a> released by Paras Lakhani</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Roberto Guerra is showing how to <a href="http://blog.stumblingoncode.com/posts/2014-11-06-ratpack-guava-eventbus.html">test the Guava EventBus with Spock</a></li>
<li>Ted Naleid on <a href="http://naleid.com/blog/2014/11/10/debugging-grails-forked-mode/">debugging Grails forked mode</a></li>
<li>Søren Berg Glasius details the <a href="http://sbglasius.tumblr.com/post/101934935847/groovyblogs">resurrection of GroovyBlogs</a>, sponsored by the GR8Conf Europe team</li>
<li><a href="http://blog.anynines.com/groovy/">Deploy a Caelyf Groovy app on Anynine&rsquo;s Cloud Foundry</a> platform</li>
</ul>
<h2 id="screencasts">Screencasts</h2>
<ul>
<li>A screencast of the <a href="http://bertram.d.pr/1fiob">Gradle asset pipeline</a> in action</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Jacob Aae Mikkelsen’s <a href="http://grydeske.net/news/show/69">Grails Diary week 45</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Cédric Champeau highlights the fact that both <a href="https://twitter.com/CedricChampeau/status/530032446415597569">Groovy traits and Markup template engine use custom type checking extensions</a></li>
<li>A new <a href="https://twitter.com/ratpackweb/status/530163157004587010">Ratpack application online for beer fans</a></li>
<li><a href="https://twitter.com/gvmtool/status/531833062699839488">Gradle 2.2</a> available on GVM</li>
<li><a href="https://twitter.com/groovyserv/status/529967998065340418">GroovyServ is now available on Homebrew</a></li>
<li><a href="https://twitter.com/groovyserv/status/531786080404516866">GroovyServ will also be available in the form of an RPM</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>This Spring Boot class shows how to <a href="https://github.com/melix/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/groovy/template/GroovyTemplateResolver.java#L40-40">create your own template resolver for Groovy&rsquo;s markup template engine</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>The <a href="https://twitter.com/greachconf/status/530662229998186496">Greach conference will take place in Madrid on April 10th-11th</a>, and its Call for Paper is open</li>
<li>Paul King will be speaking at Yow 2014 on the <a href="https://twitter.com/paulk_asert/status/530483395457843200">awesome parts of Groovy</a></li>
<li><a href="https://twitter.com/greachconf/status/530323772092710912">Kaleidos is gold sponsor of the Greach conference</a> (whose CfP is open)</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #43</title><link>https://glaforge.dev/posts/2014/11/04/groovy-weekly-43/</link><pubDate>Tue, 04 Nov 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/11/04/groovy-weekly-43/</guid><description>&lt;p>This week, an InfoWorld article lists &lt;a href="http://www.infoworld.com/article/2840235/application-development/9-cutting-edge-programming-languages-worth-learning-next.html">Groovy among the 9 cutting-edge programming language worth learning today&lt;/a>, although it reduces Groovy to a mere Java dynamic scripting language. Are you happy with the investment you made into learning Groovy a while ago?&lt;/p>
&lt;p>We also have some nice releases, like the monthly Ratpack release, or the 1.0 versions of GroovyServ (for starting up your Groovy scripts at light speed), and Gaiden for making nice documentation with Markdown.&lt;/p></description><content:encoded>
<![CDATA[<p>This week, an InfoWorld article lists <a href="http://www.infoworld.com/article/2840235/application-development/9-cutting-edge-programming-languages-worth-learning-next.html">Groovy among the 9 cutting-edge programming language worth learning today</a>, although it reduces Groovy to a mere Java dynamic scripting language. Are you happy with the investment you made into learning Groovy a while ago?</p>
<p>We also have some nice releases, like the monthly Ratpack release, or the 1.0 versions of GroovyServ (for starting up your Groovy scripts at light speed), and Gaiden for making nice documentation with Markdown.</p>
<p>Jorge Franco keeps on writing some cool demos with GrooScript, to write your JavaScript apps in your favorite programming language. He also unveiled the new <a href="http://grooscript.org/">GrooScript</a> website!</p>
<p>In the news section, I’ve also listed a couple refinements coming up in Groovy 2.4, with @DelegatesTo / @ClosureParams annotations alignments and the new @SelfType annotation for traits to restrict trait application.</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://kobo.github.io/groovyserv/changelog.html">GroovyServ 1.0</a> released</li>
<li><a href="http://www.ratpack.io/versions/0.9.10">Ratpack 0.9.10</a> released</li>
<li><a href="http://forums.gradle.org/gradle/topics/gradle-2-2-rc-2-is-now-available-for-testing?rfm=1">Gradle 2.2-rc-2</a> released fixing a couple of regressions from the previous release candidate</li>
<li><a href="https://twitter.com/grooscript/status/529365793642131457">GrooScript 0.6.2</a> released</li>
<li><a href="https://twitter.com/cedricchampeau/status/528184277306183681">Groovy android plugin for Gradle 0.3.4</a> released with support for product flavors</li>
<li><a href="https://github.com/renatoathaydes/spock-reports">Spock Reports Extension 1.2.1</a> released</li>
<li><a href="http://groovy.329449.n5.nabble.com/ANN-Gaiden-1-0-released-td5721608.html">Gaiden 1.0</a> is out, with theme, numbering, wrapper, and others</li>
<li>An updated <a href="https://registry.hub.docker.com/u/mozart/grails/">optimized Grails Docker container</a></li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>InfoWorld reports about <a href="http://www.infoworld.com/article/2840235/application-development/9-cutting-edge-programming-languages-worth-learning-next.html">9 cutting-edge programming language worth learning now, including Groovy</a></li>
<li>Roberto Guerra dives into <a href="http://blog.stumblingoncode.com/posts/2014-10-31-ratpack-guice.html">Ratpack Guice-based modularisation and dependency injection</a></li>
<li>Another great demo of <a href="http://grooscript.org/chat_example.html">Grooscript writing a Node.JS chat application with Groovy and Socket.IO</a></li>
<li>Julien Viet details the <a href="https://github.com/vert-x3/ext/blob/master/ext-rx/README.md">Rx extension for Vert.x and shows the RxGroovy integration</a> too</li>
<li>Patrick Double shares his <a href="http://www.objectpartners.com/2014/10/29/experiences-with-publishing-a-grails-plugin/">experience with publishing a Grails plugin</a></li>
<li>Albert van Veen on <a href="http://blog.jdriven.com/2014/10/grails-generate-async-controller/">Grails&rsquo; generation of asynchronous controllers</a></li>
<li>Iván López shares his tales from the GeeCON conference</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li><a href="https://speakerdeck.com/jlstrater/groovy-at-gr8ladies-iowa-code-camp">Groovy introduction</a> by the GR8 Ladies at the Iowa code camp 2014</li>
</ul>
<h2 id="screencasts">Screencasts</h2>
<ul>
<li>Bertrand Goetzmann recorded a screencast on <a href="https://www.youtube.com/watch?v=wSlCWJL0Q5I&amp;feature=youtu.be&amp;a">monitoring an Apache Camel route started from a Groovy script, with the Hawtio web console</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>A <a href="https://twitter.com/grooscript/status/527253669264502784">new look for the GrooScript website</a></li>
<li>Jacob Aae Mikkelsen <a href="http://grydeske.net/news/show/68">Grails Diary</a> week 44</li>
<li>Groovy&rsquo;s <a href="https://jira.codehaus.org/browse/GROOVY-6956">@DelegatesTo and @ClosureParams will be more aligned in Groovy 2.4</a>, the latter gaining the same parameters as the former, for more closure type checking goodness</li>
<li>Groovy 2.4 adds a <a href="https://jira.codehaus.org/browse/GROOVY-7134">@SelfType annotation to traits</a> allowing you to restrict to what types a trait can be applied</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li><a href="https://twitter.com/cedricchampeau/status/529191824796430336">Groovy works well on the new Android runtime ART</a> as well tells Cédric Champeau</li>
<li>Schalk Cronjé tweets that <a href="https://twitter.com/ysb33r/status/528184841951780864">Groovy isn&rsquo;t magical, but is an elixir to better programmer health</a></li>
<li>Robert Fletcher updated the <a href="https://twitter.com/rfletcherew/status/527498052752265217">Gradle Compass plugin</a></li>
<li>Dan Woods <a href="https://twitter.com/danveloper/status/527155055917412352">loves the new Groovy documentation</a></li>
<li><a href="https://twitter.com/gvmtool/status/529403342095925248">Gradle 2.2-rc-2</a> available on GVM</li>
<li><a href="https://twitter.com/gvmtool/status/529625692028538880">GroovyServ 1.0</a> is available on GVM</li>
<li>Dierk König <a href="https://twitter.com/mittie/status/528961665292054529">compares GPars dataflow variables to references in pure functional languages</a></li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li>André Steingreß is looking for a <a href="http://findgrailsjobs.com/job/673-software-developer-fm-java-groovy-grails-remote">Grails developer</a> in this job posting</li>
<li>Ben McGuire is <a href="https://twitter.com/ben_t_mcguire/status/527812020607787008">looking for Grails developers</a> for a major project in San Diego</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>GrailsConf India has opened its <a href="http://grailsconf.in/call-for-paper">Call for Paper</a></li>
<li>Cédric Champeau will be speaking about <a href="https://twitter.com/cedricchampeau/status/527443577388142592">Groovy for Android at Mobile Central Europe</a>, Warsaw, in February</li>
<li>Iván López is speaking about the <a href="https://twitter.com/ilopmar/status/527832608835923968">Grails framework at jDays 2015</a> in Gothenburg next March</li>
<li>The <a href="https://twitter.com/gr8ladiesmsp/status/529316231459778560">next GR8 Ladies meetup will be November 17th</a> if you&rsquo;re in the Minneapolis area</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #42</title><link>https://glaforge.dev/posts/2014/10/28/groovy-weekly-42/</link><pubDate>Tue, 28 Oct 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/10/28/groovy-weekly-42/</guid><description>&lt;p>Isn’t it a mythical number? The 42nd edition of Groovy Weekly! Will we get the answer to life the universe and everything?&lt;/p>
&lt;p>At least, we have a nice list of releases, this week, in particular with Grails 2.4.4, Geb 0.10 and the first milestone of Reactor 2.0!&lt;/p>
&lt;p>And if there’s one thing I’d like to highlight in addition, that would be MrHaki’s release of his new book, the Spock Notebook, with all those nice recipes about our preferred testing framework.&lt;/p></description><content:encoded>
<![CDATA[<p>Isn’t it a mythical number? The 42nd edition of Groovy Weekly! Will we get the answer to life the universe and everything?</p>
<p>At least, we have a nice list of releases, this week, in particular with Grails 2.4.4, Geb 0.10 and the first milestone of Reactor 2.0!</p>
<p>And if there’s one thing I’d like to highlight in addition, that would be MrHaki’s release of his new book, the Spock Notebook, with all those nice recipes about our preferred testing framework.</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://twitter.com/grailsframework/status/527119729631461376">Grails 2.4.4</a> released with bug fixes and improvements</li>
<li><a href="https://twitter.com/tomaslin/status/524798100041261058">Geb 0.10</a> released</li>
<li><a href="https://spring.io/blog/2014/10/21/reactor-2-0-0-m1-released-with-reactive-streams-integration">Reactor 2.0.0.M1</a> released</li>
<li><a href="https://twitter.com/cedricchampeau/status/526687506323238912">Groovy Android Gradle plugin 0.3.2</a> released fixing joint compilation issues</li>
<li><a href="https://twitter.com/cedricchampeau/status/527031406124609536">JMH Gradle plugin 0.1.3</a> released</li>
<li><a href="https://groups.google.com/forum/#!topic/gradle-dev/dpQkogqx_bw">Gretty 1.1.5</a> is out</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Roberto Guerra writes about <a href="http://blog.stumblingoncode.com/posts/2014-10-20-ratpack-groovy-chain.html">Ratpack handlers and Groovy chains</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Hans Dockter on <a href="https://thenewcircle.com/s/post/1673/gradle_and_the_road_ahead_hans_dockter_video">Gradle and the road ahead</a>, recorded at SF Java in June 2014</li>
<li>Cédric Champeau presented <a href="https://twitter.com/cedricchampeau/status/525608597863268353">Groovy on Android</a>, with a sample application using Google Wear, at SoftShake 2014</li>
<li>Danny Hyun <a href="https://twitter.com/Lspacewalker/status/524763500674310144">presented Ratpack</a> (slides and code included)</li>
<li>Guillaume Laforge gave his &ldquo;<a href="https://www.youtube.com/watch?v=8E3TdP_KIJo">What makes Groovy groovy</a>&rdquo; through Google Hangout at the Barcelona JUG</li>
<li>Iván López presented &ldquo;<a href="https://twitter.com/ilopmar/status/525549799638966273">Metaprogramming with Groovy</a>&rdquo; at GeeCON and offers links to his slides and code samples</li>
<li>More recordings of Trisha Gee&rsquo;s <a href="http://trishagee.github.io/presentation/angularjs_html5_groovy_java_mongodb_wcpgw/">AngularJS / HTML5 / Groovy / Java / MongoDB</a> talk</li>
</ul>
<h2 id="screencasts">Screencasts</h2>
<ul>
<li>Cédric Champeau recorded this video showing a <a href="https://vine.co/v/Ob2Z1dZrAFt">Google Wear application built with Groovy</a>, that he demonstrated at the Soft Shake 2014 conference</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Andrés Almiray repackages the huge <a href="https://github.com/aalmiray/google-materialdesignicons">Google Material Design icon as JARs that you can reuse</a> in your web / desktop / mobile projects</li>
<li>Jacob Aae Mikkelsen published the <a href="http://grydeske.net/news/show/67">Grails Diary week 43</a></li>
</ul>
<h2 id="google">Google+</h2>
<ul>
<li>A new <a href="https://plus.google.com/u/0/105332504458974739532/posts/L6edmMmQ6JR?cfem=1">CodinGame contest supporting Groovy</a> is happening soon</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Meet <a href="https://twitter.com/gradleware/status/524843728511320064">Gradle expert Peter Niederwieser in Stockholm</a></li>
<li>Use <a href="https://twitter.com/dailygrailstip/status/524964020478939136">Grails&rsquo; &ndash;non-interactive flag for easier Continuous Integration</a></li>
<li>A week into Groovy and Grails, and Yosi Pramajaya <a href="https://twitter.com/yosipramajaya/status/525469302594347008">celebrates Groovy and Grails&rsquo; awesomeness</a>!</li>
<li>Jorge Martín is looking into <a href="https://twitter.com/arasthel92/status/525914957599088640">adding some Groovy-ness to some Android APIs</a></li>
<li>Cédric Champeau wishes there were as many <a href="https://twitter.com/CedricChampeau/status/525010686951579648">contributions to the Groovy documentation</a> as there are edits on the Groovy entry on Wikipedia</li>
<li>Dierk König remarks a <a href="https://twitter.com/mittie/status/526137634671034368">programming language popularity tanking where Groovy ranks 8th</a></li>
<li><a href="https://twitter.com/Bl_nK_/status/525081570424532992">GPars makes concurrency on Groovy not suck</a></li>
<li>Burt Beckwith shares the link to a <a href="https://twitter.com/burtbeckwith/status/526440858217111553">tracker analyzing changes between Grails versions</a></li>
<li><a href="https://twitter.com/gvmtool/status/527126880714252288">Grails 2.4.4</a> available on GVM</li>
<li>Dierk König&rsquo;s been <a href="https://twitter.com/mittie/status/527115100654956544">programming in Groovy for 10 years</a> and is still amazed</li>
<li>After 10 years of Groovy, Dierk König notes that <a href="https://twitter.com/mittie/status/527129202588680193">Java finally has high-order functions</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Cédric Champeau published the code of his <a href="https://twitter.com/cedricchampeau/status/525608882253877248">Google Wear Android application written in Groovy</a></li>
<li>Dierk König presents a <a href="https://gist.github.com/Dierk/358a2bf24cdcd10044e8">null-safe monad in Groovy</a> with Java 8 function</li>
<li>Stéphane Maldini shared a snippet showing a <a href="https://gist.github.com/smaldini/d6858b08773e028f5383">send &amp; receive RabbitMQ flow with Reactor&rsquo;s AMQP</a> support</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>MrHaki published the <a href="http://mrhaki.blogspot.fr/2014/10/spocklight-notebook-is-published.html">Spock Notebook</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>The <a href="https://twitter.com/greachconf/status/525582247387480064">Greach conference is looking for sponsors</a>! If you or your company is willing to help, don&rsquo;t hesitate to get in touch with the Greach crew!</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #41</title><link>https://glaforge.dev/posts/2014/10/21/groovy-weekly-41/</link><pubDate>Tue, 21 Oct 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/10/21/groovy-weekly-41/</guid><description>&lt;p>Along with some valuable releases like the first release candidate of Gradle 2.2, there are interesting links on the topic of the Groovy Android story, as well as new glimpse on AST macros and AST pattern matching coming up. Worth a look!&lt;/p>
&lt;p>In terms of events, the Greach conference in Spain has opened its Call for Paper, and the GR8Conf Europe crew reminds us that all the great content from the last edition is freely available on their YouTube channel.&lt;/p></description><content:encoded>
<![CDATA[<p>Along with some valuable releases like the first release candidate of Gradle 2.2, there are interesting links on the topic of the Groovy Android story, as well as new glimpse on AST macros and AST pattern matching coming up. Worth a look!</p>
<p>In terms of events, the Greach conference in Spain has opened its Call for Paper, and the GR8Conf Europe crew reminds us that all the great content from the last edition is freely available on their YouTube channel.</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://twitter.com/cedricchampeau/status/524123611766878209">Groovy Android Gradle plugin 0.3.1</a> released</li>
<li><a href="http://forums.gradle.org/gradle/topics/gradle-2-2-rc-1-is-now-available-for-testing">Gradle 2.2.rc-1</a> is available</li>
<li><a href="http://groovy.329449.n5.nabble.com/ANN-Announcing-CodeNarc-0-22-td5721472.html">CodeNarc 0.22</a> released with new rules</li>
<li><a href="https://twitter.com/bsideup/status/522682180204376065">Groovy-macro-methods 0.2.0</a> released</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li><a href="http://kousenit.wordpress.com/2014/10/14/spaceships-elvis-and-groovy-inject/">Spaceships, Elvis, and Groovy inject</a> by Ken Kousen</li>
<li>An article about <a href="http://www.dexa-dev.com/groovy-saveinstance-on-swissknife/">@SaveInstance: a new AST transformation in SwissKnife</a>, the project that adds nice shortcuts for Android development with Groovy</li>
<li>MrHaki&rsquo;s Gradle Goodness: <a href="http://mrhaki.blogspot.fr/2014/10/gradle-goodness-show-standard-out-or.html">show standard out or error output from tests</a></li>
<li>MrHaki&rsquo;s Gradle Goodness: <a href="http://mrhaki.blogspot.fr/2014/10/gradle-goodness-changing-name-of.html">changing the name of the default build file</a></li>
<li><a href="http://www.eclecticlogic.com/2014/09/26/groovy-dsl-executor/">Groovier Groovy DSL</a> by Karthik Abram</li>
<li><a href="http://devops.judes.co.il/post/100396130279/artifactory-gradle-publishing-arbitrary-artifact">Publishing arbitrary artifacts with Gradle and Artifactory</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>The GR8Conf crew has published all the <a href="https://www.youtube.com/playlist?list=PLwxhnQ2Qv3xuE4JEKBpyE2AbbM_7G0EN1">videos of GR8Conf Europe 2014</a></li>
<li>Russel Winder presented <a href="https://speakerdeck.com/russelwinder/spocktacular-testing">Spock-tacular testing</a> at JAX London</li>
<li>Andrés Almiray and Ixchel Ruiz posted the slides of there JavaOne 2014 presentation titled <a href="http://fr.slideshare.net/aalmiray/javaone-gradle">Gradle: harder, better, stronger, faster</a></li>
<li><a href="http://fr.slideshare.net/gfrison/lean-reactiveserviceswithvertx">Reactive services on Vert.x with Groovy</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li><a href="http://grydeske.net/news/show/66">Grails Diary week 42</a> by Jacob Aae Mikkelsen</li>
<li>Caelyf, the lightweight toolkit for Cloud Foundry, is moving to a <a href="http://caelyf.net/">new domain</a></li>
</ul>
<h2 id="mailing-lists">Mailing-lists</h2>
<ul>
<li>Cédric Champeau gives an update on the ongoing work around Groovy AST macros, with some work around <a href="http://groovy.329449.n5.nabble.com/State-of-the-macros-td5721429.html#a5721475">AST pattern matching</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Kamil Szymański remarks that the release of <a href="https://twitter.com/kszdev/status/522662104344854529">JDK 8 update 25 solves the VerifyError affecting Groovy</a></li>
<li>Cédric Champeau shows a <a href="https://twitter.com/cedricchampeau/status/523934124898521090">Groovy demo application running on an Android smartwatch</a></li>
<li>Eugene Kamenev is working on <a href="https://twitter.com/eugenekamenev/status/523486523472109569">Grails-like domain classes for Groovy Android</a> development</li>
<li>Marcin Grzejszczak did an implementation of <a href="https://twitter.com/MGrzejszczak/status/523420864612925441">Consumer Driven Contracts in Groovy</a> at 4finance</li>
<li>The <a href="https://twitter.com/groovypuzzlers/status/524533757857267712">Groovy Puzzlers team wants to become at JavaOne rockstar</a> by collecting good survey results</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Cédric Champeau is teasing his work around <a href="https://twitter.com/cedricchampeau/status/523116956275064833">AST macros and AST tree matching</a></li>
<li>Cédric Champeau shows another example of <a href="https://gist.github.com/melix/c02227081dd60f5cedaa">AST tree matching</a></li>
<li>Guillaume Laforge shows a quick example of using the newly released CoffeeScript javax.script engine to <a href="https://gist.github.com/glaforge/f7ddece9d4e0ff2afe82">execute some CoffeeScript code from Groovy</a></li>
<li>Another <a href="https://twitter.com/grooscript/status/523856523492335617">demo of GrooScript with the sigma.js</a> API</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>The <a href="https://twitter.com/greachconf/status/524122916871368705">Call for Paper of the Greach</a> conference is open</li>
<li>Guillaume Laforge will speak at the <a href="https://twitter.com/BarcelonaJUG/status/523919077036015617">Barcelona JUG about What makes Groovy groovy</a> next Thursday</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #40</title><link>https://glaforge.dev/posts/2014/10/14/groovy-weekly-40/</link><pubDate>Tue, 14 Oct 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/10/14/groovy-weekly-40/</guid><description>&lt;p>No big highlight this week, but still plenty to learn from!&lt;/p>
&lt;p>We have some minor releases, interesting articles on Spock, on how to leverage traits with Geb, some thoughts on reactive programming, new demos and tutorials for GrooScript.&lt;/p>
&lt;p>And we can wish a happy birthday to the GR8 Ladies!&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>Spring Tool Suite &amp;amp; &lt;a href="http://docs.spring.io/sts/nan/v362/NewAndNoteworthy.html">Groovy Grails Tool Suite 3.6.2&lt;/a> released&lt;/li>
&lt;li>&lt;a href="https://twitter.com/grooscript/status/521314194319507458">GrooScript 0.6.1&lt;/a> released&lt;/li>
&lt;li>&lt;a href="https://twitter.com/grainframework/status/520515098289651712">GridGain 0.6.2&lt;/a> with better support of themes that use plain CSS and better handling of large files&lt;/li>
&lt;li>&lt;a href="https://spring.io/blog/2014/10/11/spring-boot-1-1-8-released">Spring Boot 1.1.8&lt;/a> released&lt;/li>
&lt;li>&lt;a href="https://spring.io/blog/2014/10/11/spring-boot-1-2-0-m2-available-now">Spring Boot 1.2.0.M2&lt;/a> released&lt;/li>
&lt;li>&lt;a href="https://github.com/akhikhl/gretty/blob/master/changes.md">Gretty 1.1.4&lt;/a> released&lt;/li>
&lt;li>Alain Stalder released an &lt;a href="http://groovy.329449.n5.nabble.com/ANN-Grengine-User-Manual-td5721453.html">extensive manual for his Grengine&lt;/a>, his alternative approach to integrating Groovy in your applications&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>MrHaki&amp;rsquo;s Spocklight: &lt;a href="http://mrhaki.blogspot.fr/2014/10/spocklight-indicate-class-under-test.html">indicate class under test with @Subject annotation&lt;/a>&lt;/li>
&lt;li>MrHaki&amp;rsquo;s Groovy Goodness: &lt;a href="http://mrhaki.blogspot.fr/2014/10/groovy-goodness-closure-as-class.html">Closure as a Class&lt;/a>&lt;/li>
&lt;li>Kyle Boon is leveraging &lt;a href="http://kyleboon.org/blog/2014/10/11/traitsandgeb/">Groovy traits with Geb&lt;/a>&lt;/li>
&lt;li>Luke Daley explains &lt;a href="http://ldaley.com/post/99527932537/executional-flexibility-through-reactive-programming">executional flexibility through reactive programming&lt;/a>&lt;/li>
&lt;li>A follow-up post from Andrés Almiray on the &lt;a href="http://www.jroller.com/aalmiray/entry/new_desktop_application_framework_jsr">new desktop application framework JSR&lt;/a>&lt;/li>
&lt;li>Savant, a &lt;a href="http://www.inversoft.com/blog/2014/10/08/introducing-savant-inversoft/">new Groovy-powered build tool&lt;/a>?&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://speakerdeck.com/jlstrater/2014-ghc-groovy-gr8ladies-workshop-v16">Groovy introduction&lt;/a> slides by the GR8 Ladies&lt;/li>
&lt;li>Colin Harrington on &lt;a href="http://slides.com/colinharrington/web-application-security#/">web security&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="news">News&lt;/h2>
&lt;ul>
&lt;li>Jacob Aae Mikkelsen&amp;rsquo;s &lt;a href="http://grydeske.net/news/show/65">Grails Diary&lt;/a> week 41&lt;/li>
&lt;/ul>
&lt;h2 id="interview">Interview&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/droidconuk/status/520861514396606464">Interview of Hans Dockter&lt;/a> on Gradle and continuous delivery&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/eugenekamenev/status/521007874529447937">Groovy makes Android code much more concise&lt;/a>&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/Gr8Ladies/status/520401087694917632">GR8 Ladies celebrate their first birthday&lt;/a>!&lt;/li>
&lt;li>Joe Wolf notes that &lt;a href="https://twitter.com/bdkosher/status/520295206219173888">Groovy reached 10,000 questions on Stack Overflow&lt;/a>&lt;/li>
&lt;li>Eugene Kamenev is investigating bring &lt;a href="https://twitter.com/eugenekamenev/status/521562112146276352">GORM-like features for Groovy on Android&lt;/a>&amp;lt;, to deal with building UI, handling validation, and more&lt;/li>
&lt;li>Youri Ackx liked Guillaume Laforge&amp;rsquo;s funny slide from his JavaOne presentation about how to represent &lt;a href="https://twitter.com/youriackx/status/519744695497273345">map / reduce, sandwich-style&lt;/a>&lt;/li>
&lt;li>Ratpack adds &lt;a href="https://twitter.com/ratpackweb/status/519830913106968577">more Promise operations&lt;/a> and improved the documentation&lt;/li>
&lt;li>Dierk König says the &lt;a href="https://twitter.com/mittie/status/520191158467960832">builder pattern in Groovy is a great example of the state monad&lt;/a>&lt;/li>
&lt;li>Graeme Rocher waves &lt;a href="https://twitter.com/graemerocher/status/520234393940787200">goodbye to the old Grails build system&lt;/a>&lt;/li>
&lt;li>Gradle features a handful of &lt;a href="https://twitter.com/DailyGrailsTip/status/520618252888584192">plugins for JavaScript / front-end build automation&lt;/a>&lt;/li>
&lt;li>One could wish all &lt;a href="https://twitter.com/groovylang/status/521424091447066624">front end development be done with Groovy and GrooScript&lt;/a>?&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/520927004351549440">Spring Boot 1.1.8&lt;/a> available through GVM&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/520927209415249921">Spring Boot 1.2.0.M2&lt;/a> available through GVM&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/grooscript/status/521760399398805504">GrooScript for client-side templating&lt;/a> with the HTML builder or with Groovy templates&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>Baruch Sadogursky will be speaking about AST transformations at &lt;a href="http://javaday.org.ua/#nav-speakers">JavaDay Kiev&lt;/a> and will wow the crowds with Groovy Puzzlers at JavaDay Kiev and &lt;a href="http://jokerconf.com/">Jokerconf&lt;/a>!&lt;/li>
&lt;li>Iván López will be talking about &lt;a href="http://geecon.cz/speakers/">&amp;ldquo;Metaprogramming with Groovy&amp;rdquo; at GeeCON Prague&lt;/a> next 23rd October&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/greachconf/status/521564797444190208">Greach conference&lt;/a> is being prepared and is taking shape, and the organizers are looking for sponsors&lt;/li>
&lt;li>&lt;a href="https://twitter.com/jacobaae/status/520910335570894848">GR8Conf Europe 2015&lt;/a> is in the works, as the team reunites&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>No big highlight this week, but still plenty to learn from!</p>
<p>We have some minor releases, interesting articles on Spock, on how to leverage traits with Geb, some thoughts on reactive programming, new demos and tutorials for GrooScript.</p>
<p>And we can wish a happy birthday to the GR8 Ladies!</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Spring Tool Suite &amp; <a href="http://docs.spring.io/sts/nan/v362/NewAndNoteworthy.html">Groovy Grails Tool Suite 3.6.2</a> released</li>
<li><a href="https://twitter.com/grooscript/status/521314194319507458">GrooScript 0.6.1</a> released</li>
<li><a href="https://twitter.com/grainframework/status/520515098289651712">GridGain 0.6.2</a> with better support of themes that use plain CSS and better handling of large files</li>
<li><a href="https://spring.io/blog/2014/10/11/spring-boot-1-1-8-released">Spring Boot 1.1.8</a> released</li>
<li><a href="https://spring.io/blog/2014/10/11/spring-boot-1-2-0-m2-available-now">Spring Boot 1.2.0.M2</a> released</li>
<li><a href="https://github.com/akhikhl/gretty/blob/master/changes.md">Gretty 1.1.4</a> released</li>
<li>Alain Stalder released an <a href="http://groovy.329449.n5.nabble.com/ANN-Grengine-User-Manual-td5721453.html">extensive manual for his Grengine</a>, his alternative approach to integrating Groovy in your applications</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>MrHaki&rsquo;s Spocklight: <a href="http://mrhaki.blogspot.fr/2014/10/spocklight-indicate-class-under-test.html">indicate class under test with @Subject annotation</a></li>
<li>MrHaki&rsquo;s Groovy Goodness: <a href="http://mrhaki.blogspot.fr/2014/10/groovy-goodness-closure-as-class.html">Closure as a Class</a></li>
<li>Kyle Boon is leveraging <a href="http://kyleboon.org/blog/2014/10/11/traitsandgeb/">Groovy traits with Geb</a></li>
<li>Luke Daley explains <a href="http://ldaley.com/post/99527932537/executional-flexibility-through-reactive-programming">executional flexibility through reactive programming</a></li>
<li>A follow-up post from Andrés Almiray on the <a href="http://www.jroller.com/aalmiray/entry/new_desktop_application_framework_jsr">new desktop application framework JSR</a></li>
<li>Savant, a <a href="http://www.inversoft.com/blog/2014/10/08/introducing-savant-inversoft/">new Groovy-powered build tool</a>?</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li><a href="https://speakerdeck.com/jlstrater/2014-ghc-groovy-gr8ladies-workshop-v16">Groovy introduction</a> slides by the GR8 Ladies</li>
<li>Colin Harrington on <a href="http://slides.com/colinharrington/web-application-security#/">web security</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Jacob Aae Mikkelsen&rsquo;s <a href="http://grydeske.net/news/show/65">Grails Diary</a> week 41</li>
</ul>
<h2 id="interview">Interview</h2>
<ul>
<li><a href="https://twitter.com/droidconuk/status/520861514396606464">Interview of Hans Dockter</a> on Gradle and continuous delivery</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li><a href="https://twitter.com/eugenekamenev/status/521007874529447937">Groovy makes Android code much more concise</a></li>
<li>The <a href="https://twitter.com/Gr8Ladies/status/520401087694917632">GR8 Ladies celebrate their first birthday</a>!</li>
<li>Joe Wolf notes that <a href="https://twitter.com/bdkosher/status/520295206219173888">Groovy reached 10,000 questions on Stack Overflow</a></li>
<li>Eugene Kamenev is investigating bring <a href="https://twitter.com/eugenekamenev/status/521562112146276352">GORM-like features for Groovy on Android</a>&lt;, to deal with building UI, handling validation, and more</li>
<li>Youri Ackx liked Guillaume Laforge&rsquo;s funny slide from his JavaOne presentation about how to represent <a href="https://twitter.com/youriackx/status/519744695497273345">map / reduce, sandwich-style</a></li>
<li>Ratpack adds <a href="https://twitter.com/ratpackweb/status/519830913106968577">more Promise operations</a> and improved the documentation</li>
<li>Dierk König says the <a href="https://twitter.com/mittie/status/520191158467960832">builder pattern in Groovy is a great example of the state monad</a></li>
<li>Graeme Rocher waves <a href="https://twitter.com/graemerocher/status/520234393940787200">goodbye to the old Grails build system</a></li>
<li>Gradle features a handful of <a href="https://twitter.com/DailyGrailsTip/status/520618252888584192">plugins for JavaScript / front-end build automation</a></li>
<li>One could wish all <a href="https://twitter.com/groovylang/status/521424091447066624">front end development be done with Groovy and GrooScript</a>?</li>
<li><a href="https://twitter.com/gvmtool/status/520927004351549440">Spring Boot 1.1.8</a> available through GVM</li>
<li><a href="https://twitter.com/gvmtool/status/520927209415249921">Spring Boot 1.2.0.M2</a> available through GVM</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li><a href="https://twitter.com/grooscript/status/521760399398805504">GrooScript for client-side templating</a> with the HTML builder or with Groovy templates</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>Baruch Sadogursky will be speaking about AST transformations at <a href="http://javaday.org.ua/#nav-speakers">JavaDay Kiev</a> and will wow the crowds with Groovy Puzzlers at JavaDay Kiev and <a href="http://jokerconf.com/">Jokerconf</a>!</li>
<li>Iván López will be talking about <a href="http://geecon.cz/speakers/">&ldquo;Metaprogramming with Groovy&rdquo; at GeeCON Prague</a> next 23rd October</li>
<li>The <a href="https://twitter.com/greachconf/status/521564797444190208">Greach conference</a> is being prepared and is taking shape, and the organizers are looking for sponsors</li>
<li><a href="https://twitter.com/jacobaae/status/520910335570894848">GR8Conf Europe 2015</a> is in the works, as the team reunites</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Back from JavaOne</title><link>https://glaforge.dev/talks/2014/10/07/back-from-javaone/</link><pubDate>Tue, 07 Oct 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/talks/2014/10/07/back-from-javaone/</guid><description>&lt;p>After some trouble getting to San Francisco because of strikes, transportation issues, burnt control tower, and more&amp;hellip; I managed to land to JavaOne! I missed my own first talk and I was glad and grateful that Cédric could present it for me.&lt;/p>
&lt;p>At JavaOne, the Groovy project received the award from ZeroTurnaround / RebelLabs, for the &amp;ldquo;Best Tech - Geek Choice Award&amp;rdquo; :&lt;/p>
&lt;blockquote>
&lt;p>Youpi mon projet a reçu un prix de l&amp;rsquo;innovation ! Très fier de ce que mon équipe a accompli !&lt;/p></description><content:encoded>
<![CDATA[<p>After some trouble getting to San Francisco because of strikes, transportation issues, burnt control tower, and more&hellip; I managed to land to JavaOne! I missed my own first talk and I was glad and grateful that Cédric could present it for me.</p>
<p>At JavaOne, the Groovy project received the award from ZeroTurnaround / RebelLabs, for the &ldquo;Best Tech - Geek Choice Award&rdquo; :</p>
<blockquote>
<p>Youpi mon projet a reçu un prix de l&rsquo;innovation ! Très fier de ce que mon équipe a accompli !</p>
<p><a href="https://instagram.com/p/tuU20HgNf7/">Instagram</a></p></blockquote>
<p>And at the famous &ldquo;Script Bowl&rdquo; competition, I managed to get on the first place of the podium, along with the Clojure representative, showing Groovy traits, Groovy on Android, and GrooScript transpiling Groovy into JavaScript.</p>
<blockquote>
<p><a href="https://twitter.com/hashtag/groovylang?src=hash">#groovylang</a> still first on the podium of the <a href="https://twitter.com/hashtag/JavaOne?src=hash">#JavaOne</a> script bowl, along with <a href="https://twitter.com/hashtag/clojure?src=hash">#clojure</a> too :-) Thanks to all the attendees!</p>
<p>— Guillaume Laforge (@glaforge) <a href="https://twitter.com/glaforge/status/517485161496051712">October 2, 2014</a></p></blockquote>
<p>And last but not least, my presentation slides&hellip;</p>
<script async class="speakerdeck-embed" data-id="69e2f5e02c6a013279dd1ea472fb96ba" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<script async class="speakerdeck-embed" data-id="937906e02c6a0132389e4274f6195918" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy on Android at DroidCon Paris 2014</title><link>https://glaforge.dev/talks/2014/10/07/groovy-on-android-at-droidcon-paris-2014/</link><pubDate>Tue, 07 Oct 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/talks/2014/10/07/groovy-on-android-at-droidcon-paris-2014/</guid><description>&lt;p>I was very honored to be able to speak about Groovy at DroidCon Paris 2014 a couple of weeks ago. It was really great to spend time with the Android community and learn more about their needs, pain points, and more.&lt;/p>
&lt;p>The video of the presentation is available below:&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/kOVxiob9ZgQ?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video">&lt;/iframe>
&lt;/div>
&lt;p>And you can find the slides here too:&lt;/p></description><content:encoded>
<![CDATA[<p>I was very honored to be able to speak about Groovy at DroidCon Paris 2014 a couple of weeks ago. It was really great to spend time with the Android community and learn more about their needs, pain points, and more.</p>
<p>The video of the presentation is available below:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/kOVxiob9ZgQ?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>And you can find the slides here too:</p>
<script async class="speakerdeck-embed" data-id="4a2f75502495013299fd7e85eb08a929" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #39</title><link>https://glaforge.dev/posts/2014/10/07/groovy-weekly-39/</link><pubDate>Tue, 07 Oct 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/10/07/groovy-weekly-39/</guid><description>&lt;p>Back from JavaOne, where the Groovy project received its &amp;ldquo;Best Tech - Geek Choice Awards&amp;rdquo; from ZeroTurnaround, and where it win the Script Bowl competion this year again!&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://new.griffon-framework.org/news/griffon_2.0.0.html">Griffon 2.0.0&lt;/a> final released!&lt;/li>
&lt;li>&lt;a href="http://www.ratpack.io/versions/0.9.9">Ratpack 0.9.9&lt;/a> is out moving to Java 8 as a base requirement&lt;/li>
&lt;li>Alain Stalder releases &lt;a href="http://groovy.329449.n5.nabble.com/ANN-Grengine-td5721425.html">Grengine&lt;/a>, another approach to running and embedding Groovy&lt;/li>
&lt;li>&lt;a href="https://twitter.com/RxJava/status/517187762307350528">RxGroovy 1.0.0-RC-1&lt;/a> was released with a new artifact ID&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>Marcin Zajączkowski comes back with Part 2 of &lt;a href="http://solidsoft.wordpress.com/2014/10/03/whats-new-in-upcoming-spock-1-0-part-2-cleaning-up/">what&amp;rsquo;s coming up in Spock 1.0&lt;/a>&lt;/li>
&lt;li>Andrés Almiray explains it&amp;rsquo;s &lt;a href="http://www.jroller.com/aalmiray/entry/it_s_time_for_a">time for a desktop application framework JSR&lt;/a>&lt;/li>
&lt;li>Trisha Gee is using &lt;a href="http://trishagee.github.io/post/groovy_import_to_mongodb/">Groovy to insert XML into MongoDB&lt;/a>&lt;/li>
&lt;li>Hans Dockter published the &lt;a href="http://www.gradleware.com/newsletter/gradleware-newsletter-july-aug-sep/">Gradleware July-September newsletter&lt;/a>&lt;/li>
&lt;li>André Steingreß writes about &lt;a href="http://blog.andresteingress.com/2014/10/04/grails-principal-stamp/">principals tracking in Grails&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://rpeszek.blogspot.fr/2014/10/i-dont-like-hibernategrails-part-8-but.html">Part 8&lt;/a> of the series “I don’t like Hibernate / Grails”&lt;/li>
&lt;li>A &lt;a href="http://blog.andresteingress.com/2014/10/05/grails-integration-tests/">Spock quick tip for Grails integration tests&lt;/a> from André Steingreß&lt;/li>
&lt;li>Quick Gradle tip: &lt;a href="https://andrew-oberstar.squarespace.com/blog/2014/9/28/quick-gradle-tip-setting-bintray-attributes-for-gradle-plugins">setting Bintray attributes for Gradle plugins&lt;/a>&lt;/li>
&lt;li>Tomás Lin on &lt;a href="http://fbflex.wordpress.com/2014/09/22/spring-boot-and-webjars-overwriting-configuration-during-runtime/">Spring Boot and Webjars&lt;/a>: overwriting configuration during runtime&lt;/li>
&lt;li>Burt Beckwith answers a Stack Overflow question on &lt;a href="http://stackoverflow.com/questions/26127364/override-default-grails-create-app-files/26131261#26131261">overriding Grails&amp;rsquo; default create-app files&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>Ixchel Ruiz and Andrés Almiray demonstrated the &lt;a href="http://fr.slideshare.net/aalmiray/javaone-getting-funky-with-groovy">funky-ness of Groovy&lt;/a> at JavaOne 2014&lt;/li>
&lt;li>Cédric Champeau presented “&lt;a href="https://speakerdeck.com/melix/rethinking-api-design-with-traits-in-groovy">rethinking API design with traits in Groovy&lt;/a>” at JavaOne 2014&lt;/li>
&lt;li>Guillaume Laforge posted his JavaOne 2014 slides about “&lt;a href="https://speakerdeck.com/glaforge/groovy-in-the-light-of-java-8-javaone-2014">Groovy in the light of Java 8&lt;/a>”&lt;/li>
&lt;li>Guillaume Laforge published his slides about “&lt;a href="https://speakerdeck.com/glaforge/groovy-in-2014-and-beyond-javaone-2014">Groovy in 2014 and beyond&lt;/a>” from JavaOne 2014&lt;/li>
&lt;li>Guillaume Laforge presented &lt;a href="https://www.youtube.com/watch?v=kOVxiob9ZgQ">Groovy on Android&lt;/a> at DroidCon Paris 2014&lt;/li>
&lt;/ul>
&lt;h2 id="news">News&lt;/h2>
&lt;ul>
&lt;li>Jacob Aae Mikkelsen’s &lt;a href="http://grydeske.net/news/show/63">Grails Diary week 39&lt;/a>&lt;/li>
&lt;li>Jacob Aae Mikkelsen’s &lt;a href="http://grydeske.net/news/show/64">Grails Diary week 40&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/glaforge/status/517485161496051712">Groovy reaches first place on the podium&lt;/a>, along with Clojure, beating JRuby and Scala at JavaOne 2014&amp;rsquo;s Script Bowl competition&lt;/li>
&lt;li>&lt;a href="https://twitter.com/glaforge/status/518307473766514688">Groovy received its Best Tech Geek Choice Award&lt;/a> from ZeroTurnaround at JavaOne 2014&lt;/li>
&lt;li>Cédric Champeau is looking into &lt;a href="https://twitter.com/cedricchampeau/status/519102857812144128">integrating Sergei Egorov’s Groovy macros&lt;/a> into Groovy 2.4&lt;/li>
&lt;li>Dierk König says the &lt;a href="https://twitter.com/mittie/status/517456134018318336">finish line is in sight for Groovy in Action&lt;/a>, second edition&lt;/li>
&lt;li>If you use dependency classifiers (like Groovy’s “indy”) in Gradle builds with IntelliJ IDEA, you might want to &lt;a href="https://twitter.com/SolidSoftBlog/status/515043592511844353">vote for this issue as it’s broken in Intellij IDEA&lt;/a>&lt;/li>
&lt;li>If you like &lt;a href="https://twitter.com/Lspacewalker/status/516711346801373184">SASS and Gradle&lt;/a>, Danny Hyun notes there&amp;rsquo;s a plugin for that&lt;/li>
&lt;li>Russel Hart notes that as of Ratpack 0.9.9 you can &lt;a href="https://twitter.com/rus_hart/status/517275624197865472">stream your Hystrix metrics out to the Hystrix dashboard or Turbine&lt;/a>&lt;/li>
&lt;li>Russel Hart remarks that &lt;a href="https://twitter.com/rus_hart/status/517273884845170688">Ratpack&amp;rsquo;s GroovyChainAction is replaced&lt;/a> with Action&amp;lt;Chain&amp;gt; and the Groovy.chain() call in Ratpack 0.9.9&lt;/li>
&lt;li>Craig Burke is readying his &lt;a href="https://twitter.com/craigburke1/status/519201978694778880">Angular.JS Grails Lazybones template&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>Ken Sipe’s code samples from his JavaOne 2014 &lt;a href="https://github.com/kensipe/spock-javaone2014">Spock presentation&lt;/a>&lt;/li>
&lt;li>More &lt;a href="https://twitter.com/grooscript/status/517449135897993216">GrooScript demos&lt;/a> available&lt;/li>
&lt;li>&lt;a href="https://github.com/ratpack/ratpack/commit/cc41092f7cc6?diff=split">Ratpack promises, a comparison between Java 7 and 8&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>Mac Liaw will be speaking about &lt;a href="http://1devday.net/sessions/ratpack-fun-profit/">Ratpack at 1 Dev Day Detroit&lt;/a>&lt;/li>
&lt;li>Andrés Almiray is presenting &lt;a href="http://jazoon.com/talk/Talk122/">Gradle, harder, better, stronger, faster&lt;/a>, at Jazoon&lt;/li>
&lt;/ul>
&lt;h2 id="books">Books&lt;/h2>
&lt;ul>
&lt;li>Jon Kerridge publishes &lt;a href="http://gpars-user-mailing-list.19372.n3.nabble.com/Using-Concurrency-and-Parallelism-Effectively-td4025035.html">two ebooks on concurrency and parallelism&lt;/a> and makes good use of Groovy&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>Back from JavaOne, where the Groovy project received its &ldquo;Best Tech - Geek Choice Awards&rdquo; from ZeroTurnaround, and where it win the Script Bowl competion this year again!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://new.griffon-framework.org/news/griffon_2.0.0.html">Griffon 2.0.0</a> final released!</li>
<li><a href="http://www.ratpack.io/versions/0.9.9">Ratpack 0.9.9</a> is out moving to Java 8 as a base requirement</li>
<li>Alain Stalder releases <a href="http://groovy.329449.n5.nabble.com/ANN-Grengine-td5721425.html">Grengine</a>, another approach to running and embedding Groovy</li>
<li><a href="https://twitter.com/RxJava/status/517187762307350528">RxGroovy 1.0.0-RC-1</a> was released with a new artifact ID</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Marcin Zajączkowski comes back with Part 2 of <a href="http://solidsoft.wordpress.com/2014/10/03/whats-new-in-upcoming-spock-1-0-part-2-cleaning-up/">what&rsquo;s coming up in Spock 1.0</a></li>
<li>Andrés Almiray explains it&rsquo;s <a href="http://www.jroller.com/aalmiray/entry/it_s_time_for_a">time for a desktop application framework JSR</a></li>
<li>Trisha Gee is using <a href="http://trishagee.github.io/post/groovy_import_to_mongodb/">Groovy to insert XML into MongoDB</a></li>
<li>Hans Dockter published the <a href="http://www.gradleware.com/newsletter/gradleware-newsletter-july-aug-sep/">Gradleware July-September newsletter</a></li>
<li>André Steingreß writes about <a href="http://blog.andresteingress.com/2014/10/04/grails-principal-stamp/">principals tracking in Grails</a></li>
<li><a href="http://rpeszek.blogspot.fr/2014/10/i-dont-like-hibernategrails-part-8-but.html">Part 8</a> of the series “I don’t like Hibernate / Grails”</li>
<li>A <a href="http://blog.andresteingress.com/2014/10/05/grails-integration-tests/">Spock quick tip for Grails integration tests</a> from André Steingreß</li>
<li>Quick Gradle tip: <a href="https://andrew-oberstar.squarespace.com/blog/2014/9/28/quick-gradle-tip-setting-bintray-attributes-for-gradle-plugins">setting Bintray attributes for Gradle plugins</a></li>
<li>Tomás Lin on <a href="http://fbflex.wordpress.com/2014/09/22/spring-boot-and-webjars-overwriting-configuration-during-runtime/">Spring Boot and Webjars</a>: overwriting configuration during runtime</li>
<li>Burt Beckwith answers a Stack Overflow question on <a href="http://stackoverflow.com/questions/26127364/override-default-grails-create-app-files/26131261#26131261">overriding Grails&rsquo; default create-app files</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Ixchel Ruiz and Andrés Almiray demonstrated the <a href="http://fr.slideshare.net/aalmiray/javaone-getting-funky-with-groovy">funky-ness of Groovy</a> at JavaOne 2014</li>
<li>Cédric Champeau presented “<a href="https://speakerdeck.com/melix/rethinking-api-design-with-traits-in-groovy">rethinking API design with traits in Groovy</a>” at JavaOne 2014</li>
<li>Guillaume Laforge posted his JavaOne 2014 slides about “<a href="https://speakerdeck.com/glaforge/groovy-in-the-light-of-java-8-javaone-2014">Groovy in the light of Java 8</a>”</li>
<li>Guillaume Laforge published his slides about “<a href="https://speakerdeck.com/glaforge/groovy-in-2014-and-beyond-javaone-2014">Groovy in 2014 and beyond</a>” from JavaOne 2014</li>
<li>Guillaume Laforge presented <a href="https://www.youtube.com/watch?v=kOVxiob9ZgQ">Groovy on Android</a> at DroidCon Paris 2014</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Jacob Aae Mikkelsen’s <a href="http://grydeske.net/news/show/63">Grails Diary week 39</a></li>
<li>Jacob Aae Mikkelsen’s <a href="http://grydeske.net/news/show/64">Grails Diary week 40</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li><a href="https://twitter.com/glaforge/status/517485161496051712">Groovy reaches first place on the podium</a>, along with Clojure, beating JRuby and Scala at JavaOne 2014&rsquo;s Script Bowl competition</li>
<li><a href="https://twitter.com/glaforge/status/518307473766514688">Groovy received its Best Tech Geek Choice Award</a> from ZeroTurnaround at JavaOne 2014</li>
<li>Cédric Champeau is looking into <a href="https://twitter.com/cedricchampeau/status/519102857812144128">integrating Sergei Egorov’s Groovy macros</a> into Groovy 2.4</li>
<li>Dierk König says the <a href="https://twitter.com/mittie/status/517456134018318336">finish line is in sight for Groovy in Action</a>, second edition</li>
<li>If you use dependency classifiers (like Groovy’s “indy”) in Gradle builds with IntelliJ IDEA, you might want to <a href="https://twitter.com/SolidSoftBlog/status/515043592511844353">vote for this issue as it’s broken in Intellij IDEA</a></li>
<li>If you like <a href="https://twitter.com/Lspacewalker/status/516711346801373184">SASS and Gradle</a>, Danny Hyun notes there&rsquo;s a plugin for that</li>
<li>Russel Hart notes that as of Ratpack 0.9.9 you can <a href="https://twitter.com/rus_hart/status/517275624197865472">stream your Hystrix metrics out to the Hystrix dashboard or Turbine</a></li>
<li>Russel Hart remarks that <a href="https://twitter.com/rus_hart/status/517273884845170688">Ratpack&rsquo;s GroovyChainAction is replaced</a> with Action&lt;Chain&gt; and the Groovy.chain() call in Ratpack 0.9.9</li>
<li>Craig Burke is readying his <a href="https://twitter.com/craigburke1/status/519201978694778880">Angular.JS Grails Lazybones template</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Ken Sipe’s code samples from his JavaOne 2014 <a href="https://github.com/kensipe/spock-javaone2014">Spock presentation</a></li>
<li>More <a href="https://twitter.com/grooscript/status/517449135897993216">GrooScript demos</a> available</li>
<li><a href="https://github.com/ratpack/ratpack/commit/cc41092f7cc6?diff=split">Ratpack promises, a comparison between Java 7 and 8</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>Mac Liaw will be speaking about <a href="http://1devday.net/sessions/ratpack-fun-profit/">Ratpack at 1 Dev Day Detroit</a></li>
<li>Andrés Almiray is presenting <a href="http://jazoon.com/talk/Talk122/">Gradle, harder, better, stronger, faster</a>, at Jazoon</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>Jon Kerridge publishes <a href="http://gpars-user-mailing-list.19372.n3.nabble.com/Using-Concurrency-and-Parallelism-Effectively-td4025035.html">two ebooks on concurrency and parallelism</a> and makes good use of Groovy</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #38</title><link>https://glaforge.dev/posts/2014/09/29/groovy-weekly-38/</link><pubDate>Mon, 29 Sep 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/09/29/groovy-weekly-38/</guid><description>&lt;p>Here’s a short column this week, a bit in advance, as I’m flying to JavaOne tomorrow to spread the Groovy truth!&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau announced the release of &lt;a href="http://groovy.329449.n5.nabble.com/ANN-Groovy-2-3-5-released-and-upward-compatibility-td5720556.html#a5721353">Groovy 2.3.7&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://spring.io/blog/2014/09/26/spring-boot-1-1-7-released">Spring Boot 1.1.7&lt;/a> released&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>MrHaki&amp;rsquo;s Gradle Goodness: &lt;a href="http://mrhaki.blogspot.fr/2014/09/gradle-goodness-running-groovy-scripts.html">running Groovy scripts as application&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://kousenit.wordpress.com/2014/09/17/the-reason-the-internet-was-invented-or-cat-pictures-ftw/">The reason the internet was invented or cat pictures for the win&lt;/a>, by Ken Kousen, sprinkled with bits of Groovy JSON and Swing code&lt;/li>
&lt;li>&lt;a href="http://razum.si/blog/grails-javascript-i18n-messages">Exporting Grails&amp;rsquo; i18n messages into Javascript&lt;/a>&lt;/li>
&lt;li>Andrés Almiray’s &lt;a href="http://www.jroller.com/aalmiray/entry/gradle_glam_future_features">Gradle Glam on features that would benefit the Gradle community&lt;/a>&lt;/li>
&lt;li>Jorge Martin on the &lt;a href="http://blog.arasthel.com/so-i-made-this-thing-english/">inception and the Groovy Swiss Knife library for Android and the problems encountered with Android Studio&amp;rsquo;s Groovy support&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>Guillaume Laforge presented on &lt;a href="https://speakerdeck.com/glaforge/groovy-on-android-droidcon-paris-2014">Groovy on Android&lt;/a> at DroidCon Paris 2014&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>Robert Fletcher noticed &lt;a href="https://twitter.com/rfletcherew/status/516585075077832705">IntelliJ IDEA&amp;rsquo;s clever capability of properly renaming closures it parameter&lt;/a>&lt;/li>
&lt;li>Java&amp;rsquo;s &lt;a href="https://twitter.com/aalmiray/status/516400403487809536">Duke is looking pretty Groovy&lt;/a> at JavaOne!&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/515216180333002752">Groovy 2.3.7&lt;/a> is available on GVM&lt;/li>
&lt;li>A &lt;a href="https://twitter.com/gregturn/status/514759918633881600">pretty Groovy book&lt;/a>!&lt;/li>
&lt;li>Jon DeJong &lt;a href="https://twitter.com/jondejong/status/515233596731559936">makes Groovy strongly typed by hitting the keys harder&lt;/a> when coding!&lt;/li>
&lt;li>&lt;a href="https://twitter.com/Gradleware/status/515021179640246272">New component selection and replacement rules&lt;/a> for upcoming Gradle 2.2&lt;/li>
&lt;li>Robert Guerra shows a screenshot of &lt;a href="https://twitter.com/robertoguerra19/status/514990054880866304">deployment of Ratpack with Java 8 on Heroku&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau shows a fun snippet of code using &lt;a href="https://gist.github.com/melix/5bc7c5db7ab986924181">metaprogramming to let Groovy be pretty tolerant to typos in method names&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>Here’s a short column this week, a bit in advance, as I’m flying to JavaOne tomorrow to spread the Groovy truth!</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Cédric Champeau announced the release of <a href="http://groovy.329449.n5.nabble.com/ANN-Groovy-2-3-5-released-and-upward-compatibility-td5720556.html#a5721353">Groovy 2.3.7</a></li>
<li><a href="https://spring.io/blog/2014/09/26/spring-boot-1-1-7-released">Spring Boot 1.1.7</a> released</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>MrHaki&rsquo;s Gradle Goodness: <a href="http://mrhaki.blogspot.fr/2014/09/gradle-goodness-running-groovy-scripts.html">running Groovy scripts as application</a></li>
<li><a href="http://kousenit.wordpress.com/2014/09/17/the-reason-the-internet-was-invented-or-cat-pictures-ftw/">The reason the internet was invented or cat pictures for the win</a>, by Ken Kousen, sprinkled with bits of Groovy JSON and Swing code</li>
<li><a href="http://razum.si/blog/grails-javascript-i18n-messages">Exporting Grails&rsquo; i18n messages into Javascript</a></li>
<li>Andrés Almiray’s <a href="http://www.jroller.com/aalmiray/entry/gradle_glam_future_features">Gradle Glam on features that would benefit the Gradle community</a></li>
<li>Jorge Martin on the <a href="http://blog.arasthel.com/so-i-made-this-thing-english/">inception and the Groovy Swiss Knife library for Android and the problems encountered with Android Studio&rsquo;s Groovy support</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Guillaume Laforge presented on <a href="https://speakerdeck.com/glaforge/groovy-on-android-droidcon-paris-2014">Groovy on Android</a> at DroidCon Paris 2014</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Robert Fletcher noticed <a href="https://twitter.com/rfletcherew/status/516585075077832705">IntelliJ IDEA&rsquo;s clever capability of properly renaming closures it parameter</a></li>
<li>Java&rsquo;s <a href="https://twitter.com/aalmiray/status/516400403487809536">Duke is looking pretty Groovy</a> at JavaOne!</li>
<li><a href="https://twitter.com/gvmtool/status/515216180333002752">Groovy 2.3.7</a> is available on GVM</li>
<li>A <a href="https://twitter.com/gregturn/status/514759918633881600">pretty Groovy book</a>!</li>
<li>Jon DeJong <a href="https://twitter.com/jondejong/status/515233596731559936">makes Groovy strongly typed by hitting the keys harder</a> when coding!</li>
<li><a href="https://twitter.com/Gradleware/status/515021179640246272">New component selection and replacement rules</a> for upcoming Gradle 2.2</li>
<li>Robert Guerra shows a screenshot of <a href="https://twitter.com/robertoguerra19/status/514990054880866304">deployment of Ratpack with Java 8 on Heroku</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Cédric Champeau shows a fun snippet of code using <a href="https://gist.github.com/melix/5bc7c5db7ab986924181">metaprogramming to let Groovy be pretty tolerant to typos in method names</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #37</title><link>https://glaforge.dev/posts/2014/09/23/groovy-weekly-37/</link><pubDate>Tue, 23 Sep 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/09/23/groovy-weekly-37/</guid><description>&lt;p>SpringOne2GX 2014 in Dallas is over. And next week starts JavaOne. It’s conference season! Speaking of which… the fine GR8Conf US crew released many more presentations this week! There’s a lot of content to go through!&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/grooscript/status/512724108941881344">GrooScript 0.6&lt;/a> is out, on the road to 1.0, and Jorge Franco is looking for your feedback&lt;/li>
&lt;li>&lt;a href="https://twitter.com/droidxav/status/512731018097209344">Gradle Android plugin 0.13&lt;/a> works with Gradle 2.1&lt;/li>
&lt;li>&lt;a href="https://twitter.com/javabake/status/513793973857828864">JBake 2.3.2&lt;/a> with Asciidoctor 1.5&lt;/li>
&lt;/ul>
&lt;h2 id="news">News&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://www.gradle.org/docs/2.1/release-notes#incremental-java-compilation">Incremental Java compilation landed in Gradle&lt;/a> 2.1&lt;/li>
&lt;li>&lt;a href="http://grydeske.net/news/show/62">Grails Diary week 38&lt;/a> by Jacob Aae Mikkelsen&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://solidsoft.wordpress.com/2014/09/17/whats-new-in-upcoming-spock-1-0-simpler-conditional-test-execution-with-requires-and-ignoreif/">What&amp;rsquo;s new in Spock 1.0&lt;/a>, by Marcin Zajączkowski&lt;/li>
&lt;li>&lt;a href="http://mariogarcia.github.io/gpars-workshop/#_remoting">GPars remote dataflows and actors&lt;/a> by Mario García&lt;/li>
&lt;li>&lt;a href="http://grooscript.org/react_example.html">Writing React.JS components in Groovy with GrooScript&lt;/a>&lt;/li>
&lt;li>The &amp;ldquo;&lt;a href="http://grailsrevolution.wordpress.com/2014/09/18/grails-revolution-the-force/">Grails revolution&lt;/a>&amp;rdquo; movement is looking forward to spreading the word on how great Grails is&lt;/li>
&lt;li>What&amp;rsquo;s new with &lt;a href="http://devsoap.com/#!/What-is-new-with-Gradle-Vaadin-Plugin-09">Gradle Vaadin Plugin 0.9&lt;/a>? Groovy support!&lt;/li>
&lt;li>MrHaki&amp;rsquo;s Gradle Goodness: &lt;a href="http://mrhaki.blogspot.fr/2014/09/gradle-goodness-adding-dependencies.html">Adding dependencies only for packaging to war&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://csiebler.github.io/blog/2014/02/09/multi-project-code-coverage-using-gradle-and-jacoco/">Multi-project code coverage using Gradle and JaCoCo&lt;/a> by Clemens Siebler&lt;/li>
&lt;li>After his Hibernate/Grails series, Robert Peszek comes to functional programming: &amp;ldquo;My dream: software without any bugs &amp;hellip; and &lt;a href="http://rpeszek.blogspot.fr/2014/09/my-dream-software-without-any-bugs-and.html">is Groovy functional? How about Grails&lt;/a>?&amp;rdquo;&lt;/li>
&lt;li>How to &lt;a href="https://www.timroes.de/2013/09/12/speed-up-gradle/">speed up Gradle builds? Use the daemon&lt;/a>!&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>Guillaume Laforge presented about &lt;a href="https://speakerdeck.com/glaforge/groovy-on-android-droidcon-paris-2014">Groovy&amp;rsquo;s Android support&lt;/a> at DroidCon Paris 2014&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=RuTupC0I59M&amp;amp;feature=youtu.be">Idiomatic Spock&lt;/a> with Robert Fletcher recorded at Netflix&lt;/li>
&lt;li>Trisha Gee gave her &lt;a href="http://virtualjug.com/html5-angularjs-groovy-java-and-mongodb-all-together-what-could-go-wrong/">Groovy / Angular.JS / HTML 5 / MongoDB&lt;/a> talk to the Virtual JUG&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=f_fGwa2-rMk">Micro-services with Groovy and Spring Boot&lt;/a> by Marcin Grzejszczak&lt;/li>
&lt;li>GR8Conf US 2014 presentations
&lt;ul>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=0aYcLMJkv5c">Groovy at SmartThings&lt;/a> presented by Scott Vlaminck and Ryan Applegate&lt;/li>
&lt;li>The &lt;a href="https://www.youtube.com/watch?v=O0vt57XeF5E&amp;amp;feature=youtu.be">Groovy Ecosystem revisited&lt;/a> by Andrés Almiray&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=g0fX5yA-Xzg&amp;amp;feature=youtu.be">Getting started with Ratpack&lt;/a> presented by Jeff Beck&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=Ap16ueXSJig">Realtime Ratpack&lt;/a> by Robert Fletcher&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=G4EAd514jdE">Introduction to Gradle&lt;/a> by John Engelman&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=qyj7A-8n49w">Grails Goodness&lt;/a> presented by Hubert Klein Ikkink&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=erg5Ng8yuKQ">Gradle Goodness&lt;/a> presented by Hubert Klein Ikkink&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=u3KpiLZ1-64&amp;amp;feature=youtu.be">Distributed platform development with Groovy&lt;/a> by Robert Fletcher&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=unUJwAAtLcM">Introduction to multithreaded programing with GPars&lt;/a> by Jon DeJong&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=ZYISj1RmQA0&amp;amp;feature=youtu.be">Tour de plugin&lt;/a>, by Søren Berg Glasius&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=tSbx2MSsD5A">Grails Worst Practices&lt;/a> by Burt Beckwith&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=QhVCYLgHak4&amp;amp;feature=youtu.be">Griffon, what&amp;rsquo;s new and what&amp;rsquo;s coming&lt;/a>, by Andrés Almiray&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=u8Ib2JFcyX8">How to test your Grails application&lt;/a> presented by Colin Harrington&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=UlJByqywIkE&amp;amp;feature=youtu.be">Hybrid view rendering with Grails&lt;/a> by Robert Fletcher&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=lL_wQF5AjH8">Testing BitCoin and MasterCoin with Spock&lt;/a> by Sean Gilligan&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=EnyKt7NQTEY">Performance tuning Grails applications&lt;/a> by Lari Hotari&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=1umis5WS8_w">What&amp;rsquo;s new in the Grails Spring security plugin&lt;/a> by Burt Beckwith&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=Rid_hOnJTrk">Application architectures in Grails&lt;/a> by Peter Ledbrook&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=dV-a21JQ21k">Idiomatic Spock&lt;/a> by Robert Fletcher&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=hHMaTZarRjY">Getting Grails into the Enterprise&lt;/a> by Ryan Vanderwerf&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=YG1hWfLDuBo">Metaprogramming with the Groovy Runtime&lt;/a> by Jeff Brown&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=OFFjqeqt8Ds">Getting Groovy with Graphs&lt;/a> by Stefan Armbruster&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=LyJ4fORkHfA">Serving Websockets to iOS, Android and the Web using Groovy, Spring, Angular.js, SockJs and STOMP&lt;/a> presented by Sean Gilligan&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="screencasts">Screencasts&lt;/h2>
&lt;ul>
&lt;li>Bertrand Goetzmann publishes a screen cast of &lt;a href="https://www.youtube.com/watch?v=QHGqxMpxqMU&amp;amp;feature=youtu.be">Grails with the Ionic framework&lt;/a> demonstrating a REST API example&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>Peter Niederwieser is &lt;a href="https://twitter.com/pniederw/status/513084438582800385">lobbying for fixing those issues in the IntelliJ IDEA Groovy support&lt;/a>. Please vote for them!&lt;/li>
&lt;li>&lt;a href="https://twitter.com/ratpackweb/status/514200501890457600">Ratpack 0.9.9 onward will require Java 8&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/ratpackweb/status/514243729192779778">Ratpack ships new releases every first day of the month&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/ratpackweb/status/514066358959501313">Ratpack’s RxJava support in Java 7 vs Java 8&lt;/a>&lt;/li>
&lt;li>You can decorate your garden with &lt;a href="https://twitter.com/finistseb/status/511984302809444352">Groovy flowers&lt;/a>!&lt;/li>
&lt;li>Proper &lt;a href="https://twitter.com/cedricchampeau/status/512941179395399681">overloaded setter support&lt;/a> is coming to Groovy&lt;/li>
&lt;li>&lt;a href="https://twitter.com/_ericlobdell/status/513299775538864129">Groovy as a gateway drug into the JVM&lt;/a>&lt;/li>
&lt;li>With OpenJDK 8 out, &lt;a href="https://twitter.com/ratpackweb/status/512365480070221824">Ratpack&amp;rsquo;s next release will require Java 8&lt;/a>&lt;/li>
&lt;li>Benoît Hédiart mentions CodingGame.com: a &lt;a href="https://twitter.com/benorama/status/512541644269584384">coding game where you can program games in Groovy&lt;/a>&lt;/li>
&lt;li>Guillaume Laforge is having fun coding an &lt;a href="https://twitter.com/glaforge/status/513782559919341568">Android application in Groovy for helping her daughter to learn how to read&lt;/a>&lt;/li>
&lt;li>Guillaume Laforge on stage at &lt;a href="https://twitter.com/droidconfr/status/514073374742818816">DroidCon Paris 2014 evangelizing Groovy on Android&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/sbglasius/status/513834602746478592">GroovyBlogs now displays latest blog entries and most popular&lt;/a> ones&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>How to create &lt;a href="http://stackoverflow.com/questions/23614095/using-gradle-to-split-external-libraries-in-separated-dex-files-to-solve-android/25975702#25975702">multiple Android dex files with a Gradle trick&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>SpringOne2GX 2014 in Dallas is over. And next week starts JavaOne. It’s conference season! Speaking of which… the fine GR8Conf US crew released many more presentations this week! There’s a lot of content to go through!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://twitter.com/grooscript/status/512724108941881344">GrooScript 0.6</a> is out, on the road to 1.0, and Jorge Franco is looking for your feedback</li>
<li><a href="https://twitter.com/droidxav/status/512731018097209344">Gradle Android plugin 0.13</a> works with Gradle 2.1</li>
<li><a href="https://twitter.com/javabake/status/513793973857828864">JBake 2.3.2</a> with Asciidoctor 1.5</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li><a href="http://www.gradle.org/docs/2.1/release-notes#incremental-java-compilation">Incremental Java compilation landed in Gradle</a> 2.1</li>
<li><a href="http://grydeske.net/news/show/62">Grails Diary week 38</a> by Jacob Aae Mikkelsen</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li><a href="http://solidsoft.wordpress.com/2014/09/17/whats-new-in-upcoming-spock-1-0-simpler-conditional-test-execution-with-requires-and-ignoreif/">What&rsquo;s new in Spock 1.0</a>, by Marcin Zajączkowski</li>
<li><a href="http://mariogarcia.github.io/gpars-workshop/#_remoting">GPars remote dataflows and actors</a> by Mario García</li>
<li><a href="http://grooscript.org/react_example.html">Writing React.JS components in Groovy with GrooScript</a></li>
<li>The &ldquo;<a href="http://grailsrevolution.wordpress.com/2014/09/18/grails-revolution-the-force/">Grails revolution</a>&rdquo; movement is looking forward to spreading the word on how great Grails is</li>
<li>What&rsquo;s new with <a href="http://devsoap.com/#!/What-is-new-with-Gradle-Vaadin-Plugin-09">Gradle Vaadin Plugin 0.9</a>? Groovy support!</li>
<li>MrHaki&rsquo;s Gradle Goodness: <a href="http://mrhaki.blogspot.fr/2014/09/gradle-goodness-adding-dependencies.html">Adding dependencies only for packaging to war</a></li>
<li><a href="http://csiebler.github.io/blog/2014/02/09/multi-project-code-coverage-using-gradle-and-jacoco/">Multi-project code coverage using Gradle and JaCoCo</a> by Clemens Siebler</li>
<li>After his Hibernate/Grails series, Robert Peszek comes to functional programming: &ldquo;My dream: software without any bugs &hellip; and <a href="http://rpeszek.blogspot.fr/2014/09/my-dream-software-without-any-bugs-and.html">is Groovy functional? How about Grails</a>?&rdquo;</li>
<li>How to <a href="https://www.timroes.de/2013/09/12/speed-up-gradle/">speed up Gradle builds? Use the daemon</a>!</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Guillaume Laforge presented about <a href="https://speakerdeck.com/glaforge/groovy-on-android-droidcon-paris-2014">Groovy&rsquo;s Android support</a> at DroidCon Paris 2014</li>
<li><a href="https://www.youtube.com/watch?v=RuTupC0I59M&amp;feature=youtu.be">Idiomatic Spock</a> with Robert Fletcher recorded at Netflix</li>
<li>Trisha Gee gave her <a href="http://virtualjug.com/html5-angularjs-groovy-java-and-mongodb-all-together-what-could-go-wrong/">Groovy / Angular.JS / HTML 5 / MongoDB</a> talk to the Virtual JUG</li>
<li><a href="https://www.youtube.com/watch?v=f_fGwa2-rMk">Micro-services with Groovy and Spring Boot</a> by Marcin Grzejszczak</li>
<li>GR8Conf US 2014 presentations
<ul>
<li><a href="https://www.youtube.com/watch?v=0aYcLMJkv5c">Groovy at SmartThings</a> presented by Scott Vlaminck and Ryan Applegate</li>
<li>The <a href="https://www.youtube.com/watch?v=O0vt57XeF5E&amp;feature=youtu.be">Groovy Ecosystem revisited</a> by Andrés Almiray</li>
<li><a href="https://www.youtube.com/watch?v=g0fX5yA-Xzg&amp;feature=youtu.be">Getting started with Ratpack</a> presented by Jeff Beck</li>
<li><a href="https://www.youtube.com/watch?v=Ap16ueXSJig">Realtime Ratpack</a> by Robert Fletcher</li>
<li><a href="https://www.youtube.com/watch?v=G4EAd514jdE">Introduction to Gradle</a> by John Engelman</li>
<li><a href="https://www.youtube.com/watch?v=qyj7A-8n49w">Grails Goodness</a> presented by Hubert Klein Ikkink</li>
<li><a href="https://www.youtube.com/watch?v=erg5Ng8yuKQ">Gradle Goodness</a> presented by Hubert Klein Ikkink</li>
<li><a href="https://www.youtube.com/watch?v=u3KpiLZ1-64&amp;feature=youtu.be">Distributed platform development with Groovy</a> by Robert Fletcher</li>
<li><a href="https://www.youtube.com/watch?v=unUJwAAtLcM">Introduction to multithreaded programing with GPars</a> by Jon DeJong</li>
<li><a href="https://www.youtube.com/watch?v=ZYISj1RmQA0&amp;feature=youtu.be">Tour de plugin</a>, by Søren Berg Glasius</li>
<li><a href="https://www.youtube.com/watch?v=tSbx2MSsD5A">Grails Worst Practices</a> by Burt Beckwith</li>
<li><a href="https://www.youtube.com/watch?v=QhVCYLgHak4&amp;feature=youtu.be">Griffon, what&rsquo;s new and what&rsquo;s coming</a>, by Andrés Almiray</li>
<li><a href="https://www.youtube.com/watch?v=u8Ib2JFcyX8">How to test your Grails application</a> presented by Colin Harrington</li>
<li><a href="https://www.youtube.com/watch?v=UlJByqywIkE&amp;feature=youtu.be">Hybrid view rendering with Grails</a> by Robert Fletcher</li>
<li><a href="https://www.youtube.com/watch?v=lL_wQF5AjH8">Testing BitCoin and MasterCoin with Spock</a> by Sean Gilligan</li>
<li><a href="https://www.youtube.com/watch?v=EnyKt7NQTEY">Performance tuning Grails applications</a> by Lari Hotari</li>
<li><a href="https://www.youtube.com/watch?v=1umis5WS8_w">What&rsquo;s new in the Grails Spring security plugin</a> by Burt Beckwith</li>
<li><a href="https://www.youtube.com/watch?v=Rid_hOnJTrk">Application architectures in Grails</a> by Peter Ledbrook</li>
<li><a href="https://www.youtube.com/watch?v=dV-a21JQ21k">Idiomatic Spock</a> by Robert Fletcher</li>
<li><a href="https://www.youtube.com/watch?v=hHMaTZarRjY">Getting Grails into the Enterprise</a> by Ryan Vanderwerf</li>
<li><a href="https://www.youtube.com/watch?v=YG1hWfLDuBo">Metaprogramming with the Groovy Runtime</a> by Jeff Brown</li>
<li><a href="https://www.youtube.com/watch?v=OFFjqeqt8Ds">Getting Groovy with Graphs</a> by Stefan Armbruster</li>
<li><a href="https://www.youtube.com/watch?v=LyJ4fORkHfA">Serving Websockets to iOS, Android and the Web using Groovy, Spring, Angular.js, SockJs and STOMP</a> presented by Sean Gilligan</li>
</ul>
</li>
</ul>
<h2 id="screencasts">Screencasts</h2>
<ul>
<li>Bertrand Goetzmann publishes a screen cast of <a href="https://www.youtube.com/watch?v=QHGqxMpxqMU&amp;feature=youtu.be">Grails with the Ionic framework</a> demonstrating a REST API example</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Peter Niederwieser is <a href="https://twitter.com/pniederw/status/513084438582800385">lobbying for fixing those issues in the IntelliJ IDEA Groovy support</a>. Please vote for them!</li>
<li><a href="https://twitter.com/ratpackweb/status/514200501890457600">Ratpack 0.9.9 onward will require Java 8</a></li>
<li><a href="https://twitter.com/ratpackweb/status/514243729192779778">Ratpack ships new releases every first day of the month</a></li>
<li><a href="https://twitter.com/ratpackweb/status/514066358959501313">Ratpack’s RxJava support in Java 7 vs Java 8</a></li>
<li>You can decorate your garden with <a href="https://twitter.com/finistseb/status/511984302809444352">Groovy flowers</a>!</li>
<li>Proper <a href="https://twitter.com/cedricchampeau/status/512941179395399681">overloaded setter support</a> is coming to Groovy</li>
<li><a href="https://twitter.com/_ericlobdell/status/513299775538864129">Groovy as a gateway drug into the JVM</a></li>
<li>With OpenJDK 8 out, <a href="https://twitter.com/ratpackweb/status/512365480070221824">Ratpack&rsquo;s next release will require Java 8</a></li>
<li>Benoît Hédiart mentions CodingGame.com: a <a href="https://twitter.com/benorama/status/512541644269584384">coding game where you can program games in Groovy</a></li>
<li>Guillaume Laforge is having fun coding an <a href="https://twitter.com/glaforge/status/513782559919341568">Android application in Groovy for helping her daughter to learn how to read</a></li>
<li>Guillaume Laforge on stage at <a href="https://twitter.com/droidconfr/status/514073374742818816">DroidCon Paris 2014 evangelizing Groovy on Android</a></li>
<li><a href="https://twitter.com/sbglasius/status/513834602746478592">GroovyBlogs now displays latest blog entries and most popular</a> ones</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>How to create <a href="http://stackoverflow.com/questions/23614095/using-gradle-to-split-external-libraries-in-separated-dex-files-to-solve-android/25975702#25975702">multiple Android dex files with a Gradle trick</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #36</title><link>https://glaforge.dev/posts/2014/09/16/groovy-weekly-36/</link><pubDate>Tue, 16 Sep 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/09/16/groovy-weekly-36/</guid><description>&lt;p>The SpringOne2GX 2014 conference is over, but you can see below more tweets and presentations from the second part of the conference, and the videos will be available later on on InfoQ.&lt;/p>
&lt;p>Apart from the excitement around Grails 3, Spring Boot, Groovy, and more, I’d like to highlight the very interesting feedback from Scott Hickey who comes back on 6 years of production usage of Groovy at Mutual of Omaha, saying “Groovy is a phenomenal language for businnes applications”, and that there’s nothing “better” on the JVM! See the first couple of tweets in the Twitter section.&lt;/p></description><content:encoded>
<![CDATA[<p>The SpringOne2GX 2014 conference is over, but you can see below more tweets and presentations from the second part of the conference, and the videos will be available later on on InfoQ.</p>
<p>Apart from the excitement around Grails 3, Spring Boot, Groovy, and more, I’d like to highlight the very interesting feedback from Scott Hickey who comes back on 6 years of production usage of Groovy at Mutual of Omaha, saying “Groovy is a phenomenal language for businnes applications”, and that there’s nothing “better” on the JVM! See the first couple of tweets in the Twitter section.</p>
<h2 id="news">News</h2>
<ul>
<li>With Gradle 2.1, you can <a href="http://www.gradle.org/docs/2.1/release-notes#groovy-compiler-configuration-script-support">type check or statically compiled all your Groovy code by default</a></li>
<li>Oracle mentions briefly the <a href="https://blogs.oracle.com/henrik/entry/java_8_not_just_for">collaboration between Oracle and the Groovy development team to test JDK 8</a></li>
<li><a href="http://grydeske.net/news/show/61">Grails Diary</a> week 37 by Jacob Aae Mikkelsen</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Luke Daley explains the <a href="http://ldaley.com/post/97376696242/ratpack-execution-model-part-1">Ratpack execution model</a></li>
<li>Jeff Beck details <a href="http://beckje01.com/blog/2014/09/10/ratpack-promise/">Ratpack promises</a></li>
<li>Roberto Guerra dives into how to replicate an <a href="http://blog.stumblingoncode.com/posts/2014-09-11-ratpack-asset-processing.html">asset-pipeline for Ratpack</a></li>
<li>Mark Perry writes on <a href="http://mperry.github.io/2014/09/10/groovy-monad-combinators.html">Groovy Monad Combinators</a></li>
<li><a href="http://rpeszek.blogspot.fr/2014/09/i-dont-like-hibernategrails-part-7.html">I don&rsquo;t like Grails / Hibernate</a>, part 7, working on more complex projects</li>
<li><a href="http://www.itexto.net/devkico/?p=1957">Integrate Groovy and Java</a> by Henrique Lobo Weissmann in Portuguese</li>
<li>SpringOne2GX 2014 recap by Uday Pratap Singh
<ul>
<li><a href="http://www.intelligrape.com/blog/2014/09/09/day-1-at-springone2gx-2014-opening-night-keynotes/">Day #1</a></li>
<li><a href="http://www.intelligrape.com/blog/2014/09/10/day-2-at-springone-2gx-2014/">Day #2</a></li>
<li><a href="http://www.intelligrape.com/blog/2014/09/11/day-3-at-springone-2gx-2014/">Day #3</a></li>
<li><a href="http://www.intelligrape.com/blog/2014/09/12/day-4-at-springone-2gx-2014-the-finale/">Day #4</a></li>
</ul>
</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Andrew Reitz posts his <a href="https://twitter.com/andrewreitz_/status/509543532181610496">Groovy on Android</a> lightning talk slides</li>
<li>Andrés Almiray presented <a href="http://2014.javazone.no/presentation.html?id=afbc91ce">Functional Groovy</a> at the JavaZone conference</li>
<li>GR8Conf US 2014 videos
<ul>
<li>Kyle Boon on <a href="https://www.youtube.com/watch?v=nVT8Gw-7x_c&amp;feature=youtu.be">growing up with Grails</a></li>
<li>Ryan Applegate and Adam Lanners explain how to <a href="https://www.youtube.com/watch?v=x2-yF-L4gdQ">get Groovy with Vert.x</a></li>
</ul>
</li>
<li>SpringOne2GX 2014 presentations
<ul>
<li>Cédric Champeau posted the <a href="https://twitter.com/cedricchampeau/status/510166855994990593">slides and code of his Groovy on Android</a> presentation</li>
<li><a href="https://speakerdeck.com/graemerocher/making-spring-boot-groovier">Making Spring Boot groovier</a> by Graeme Rocher</li>
<li><a href="https://speakerdeck.com/graemerocher/advanced-gorm-beyond-relational">Advanced Grails GORM beyond relational</a> by Graeme Rocher</li>
<li>Graeme Rocher reveals a <a href="https://speakerdeck.com/graemerocher/grails-3-preview">preview of Grails 3.0</a></li>
<li>Marco Vermeulen presented <a href="http://marcovermeulen.github.io/cuke-groovy-talk/#/">Behavior-Driven-Development with Cucumber and Groovy</a></li>
<li>Marco Vermeulen posted the code and slides of his <a href="https://twitter.com/marcovermeulen/status/510089484545687552">Spring Boot + Groovy + Cucumber</a> talk</li>
<li>Stéphane Maldini posted the slides of his presentation on <a href="http://fr.slideshare.net/StphaneMaldini/spring-one2gx-2014reactivestreams">Reactor and Reactive Streams</a></li>
<li>Lari Hotari&rsquo;s <a href="https://twitter.com/lhotari/status/510569280606199808">performance tuning talk</a>: on unnecessary block operations</li>
<li>Lari Hotari&rsquo;s <a href="https://twitter.com/lhotari/status/510568222190354432">performance tuning presentation</a>: on CPU-hogging regular expressions</li>
<li>Fabrice Matrat shares his presentation on <a href="http://fabricematrat.github.io/grails-rave/">RaveJS and Grails</a></li>
</ul>
</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>SpringOne2GX 2014 Groovy BOF started with the case study of Groovy production usage at Mutual of Omaha</li>
<li>Testimonial from the <a href="https://twitter.com/glaforge/status/509533986897399808">6 years of Groovy in production at Mutual of Omaha</a>: &ldquo;We learned Groovy is better, period.&rdquo;</li>
<li>&ldquo;<a href="https://twitter.com/glaforge/status/509535048341520384">Groovy is a phenomenal language for complicated business applications</a>&rdquo; says Mutual of Omaha</li>
<li><a href="https://twitter.com/sbglasius/status/510075936964763648">Netflix is contributing heavily to the Gradle plugin ecosystem</a></li>
<li>Jack Frosch finds Groovy is getting better and better, especially with <a href="https://twitter.com/jackfrosch/status/509540543278235648">traits as a &ldquo;game changer&rdquo;</a></li>
<li>Stéphane Maldini mentions the ongoing <a href="https://twitter.com/smaldini/status/511171875649880065">collaboration between the Groovy development team and the Reactor team to make Reactor play nice with Groovy on Android</a></li>
<li>You can <a href="https://twitter.com/sbglasius/status/509839267711840256">send Groovy puzzlers</a> by email and through twitter</li>
<li>Szymon Stepniak shares a <a href="https://twitter.com/wololock/status/509605494869463040">recipe for having a good time: Groovy + GPars + Spring Boot</a></li>
<li>Andrés Almiray notes <a href="https://twitter.com/aalmiray/status/510010512726585344">issues with JDK 8 update 11 with Groovy and Cobertura</a> and decides to move to JaCoCo instead</li>
<li><a href="https://twitter.com/ratpackweb/status/510037090344501248">Ratpack is making promises transformable</a> making async easier</li>
<li><a href="https://twitter.com/gvmtool/status/510041384569409536">GVM asks your feedback in a survey</a> asking you if you prefer lite or full distributions when downloading candidates</li>
<li>Dierk König remarks that <a href="https://twitter.com/mittie/status/510099003779055616">Intellij IDEA shows Java constructor calls like Groovy named-parameter constructor calls</a></li>
<li>Dierk König teases with a <a href="https://twitter.com/mittie/status/511289143243337728">GroovyFX demo</a> for his upcoming JavaOne presentation</li>
<li>Cédric Champeau will be speaking about <a href="https://twitter.com/cedricchampeau/status/511909801811398656">Groovy on Android at SoftShake</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>SpringOne2GX 2014 <a href="https://github.com/SpringOne2GX-2014/reactive-geocoder">demo application combining Ratpack, Spring Boot, Reactor and its reactive streams on Java 8</a></li>
<li>Václav Pech illustrates a <a href="https://gist.github.com/vaclav/c93225b2f93263bb8c55">safe thread starter with a Groovy trait</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>The 3rd <a href="https://twitter.com/gradleware/status/511641094442860544">Gradle Summit</a> will be held June 11-12th in Santa Clara, California</li>
<li><a href="https://twitter.com/colinharrington/status/509754307009581056">SpringOne2GX 2015 will talk place in Washington DC</a> on September 14th-17th</li>
<li>The <a href="https://twitter.com/jennstrater/status/509758555142889472">GR8Ladies now have a monthly meetup</a>. Next talk on October 6th with an introduction to Groovy</li>
<li>The Austin Groovy Grails user group wraps up its <a href="http://www.meetup.com/Austin-Groovy-and-Grails-Users/events/206899722/">coverage of the SpringOne2GX 2014 conference</a> on September 24th</li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li><a href="http://careers.stackoverflow.com/jobs/67807/160k-android-developer-work-with-android-l-averity">Work with RxJava, Groovy on Android for the New York Times</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #35</title><link>https://glaforge.dev/posts/2014/09/09/groovy-weekly-34/</link><pubDate>Tue, 09 Sep 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/09/09/groovy-weekly-34/</guid><description>&lt;p>Direct live from &lt;a href="http://springone2gx.com/">SpringOne2GX&lt;/a>, in Dallas! So you can expect some news and presentations from the show this week and the next, and certainly tons of tweets!&lt;/p>
&lt;p>Speaking of tweets from SpringOne2GX, you’ll notice the flurry of very positive and excited reactions regarding the upcoming Grails 3 release.&lt;/p>
&lt;p>In the release section, don’t miss the release of Gradle 2.1.&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://forums.gradle.org/gradle/topics/gradle-2-1-released">Gradle 2.1&lt;/a> released with community plugins, and incremental java compilation&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/506086805759066112">GVM 2.1&lt;/a> released with improved self-updates&lt;/li>
&lt;li>&lt;a href="https://github.com/Arasthel/SwissKnife">Swiss Army&lt;/a>, a handy library for Groovy on Android, covering view injection, threading and more, by Jorge Martín&lt;/li>
&lt;li>&lt;a href="http://forums.gradle.org/gradle/topics/gradle-2-1-rc-4-is-now-available-for-testing">Gradle 2.1 rc-4&lt;/a> is ready for testing&lt;/li>
&lt;li>&lt;a href="https://spring.io/blog/2014/09/05/spring-boot-1-1-6-released">Spring Boot 1.1.6&lt;/a> released&lt;/li>
&lt;li>A &lt;a href="https://github.com/robertpanzer/asciidoctorj-groovy-dsl">Groovy DSL that allows for easy definition of Asciidoctor extensions&lt;/a> by Robert Panzer&lt;/li>
&lt;li>&lt;a href="https://twitter.com/aalmiray/status/507818302496702465">Asciidoctor Gradle Template 1.0.0&lt;/a> announced by Andrés Almiray&lt;/li>
&lt;/ul>
&lt;h2 id="news">News&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://www.groovyblogs.org/">GroovyBlogs&lt;/a> is coming back to life thanks to Søren Berg Glasius&lt;/li>
&lt;li>Jacob Aae Mikkelsen &lt;a href="http://grydeske.net/news/show/60">Grails Diary week 36&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://docs.codehaus.org/display/GPARS/2014/09/06/Remoting+for+GPars">Remoting comes to GPars&lt;/a>, thanks to Google Summer of Code Rafał Sławik&amp;rsquo;s great work&lt;/li>
&lt;li>&lt;a href="http://kyleboon.org/blog/2014/08/14/ratpack-plus-docker-plus-gradle/">Ratpack + Docker + Gradle&lt;/a> by Kyle Boon&lt;/li>
&lt;li>Benjamin Muschko speaks about &lt;a href="http://www.gradleware.com/tutorials/feature-spotlight-gradles-support-maven-pom-profiles/">Gradle&amp;rsquo;s support of Maven POM profiles&lt;/a>&lt;/li>
&lt;li>&amp;ldquo;I don&amp;rsquo;t like Grails / Hibernate&amp;rdquo; goes on, covering &lt;a href="http://rpeszek.blogspot.fr/2014/09/i-dont-like-hibernategrails-part-6-how.html">how to save objects using refresh()&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://www.gradleware.com/conferences/gradle-summit-2014-video-gradle-enterprise-linkedin/">Gradle in the enterprise at LinkedIn&lt;/a> at Gradle Summit 2014&lt;/li>
&lt;li>&lt;a href="http://www.parleys.com/play/530b52fbe4b085f68c6b653f/chapter0/about">Benefit from Groovy, when, why, and how&lt;/a>, presented by Guillaume Laforge at JFokus 2014&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/ShlomiBenHaim/status/508772445852278784">Groovy way to write Jenkins CI plugins&lt;/a> by Shiran Rubin&lt;/li>
&lt;li>&lt;a href="http://www.infoq.com/fr/presentations/programmation-fonctionnelle-groovy">Functional programming in Groovy&lt;/a>, by Guillaume Laforge, recorded at BreizhCamp 2014 (in French)&lt;/li>
&lt;li>&lt;a href="http://jaxenter.de/videos/funktionen-monaden-groovy-jaxtv-175429">How to get fully functional in Groovy&lt;/a>, by Dierk König (in German)&lt;/li>
&lt;li>Video tutorial how &lt;a href="http://www.gradleware.com/conferences/gradle-summit-2014-video-gradle-ides/">Gradle is integrated in Java IDEs&lt;/a>&lt;/li>
&lt;li>A &lt;a href="http://www.java-tv.com/2014/08/28/groovy-introduction/">Groovy introduction presentation&lt;/a> by Joachim Baumann&lt;/li>
&lt;li>First slide decks published from SpringOne2GX 2014 in Dallas, Texas:
&lt;ul>
&lt;li>Cédric Champeau dived into &lt;a href="https://speakerdeck.com/melix/rethinking-api-design-with-traits">rethinking API design with Groovy traits&lt;/a>&lt;/li>
&lt;li>Guillaume Laforge presented the &lt;a href="https://speakerdeck.com/glaforge/groovy-in-2014-and-beyond-at-springone2gx-2014">latest state of the Groovy nation&lt;/a> at SpringOne2GX 2014&lt;/li>
&lt;li>Guillaume Laforge answers the question wheter &lt;a href="https://speakerdeck.com/glaforge/groovy-in-the-light-of-java-8-springone2gx-2014">Groovy is still relevant now that we have Java 8&lt;/a>&lt;/li>
&lt;li>Paul King speaks about &lt;a href="http://fr.slideshare.net/paulk_asert/groovy-databases">working with databases in Groovy&lt;/a>&lt;/li>
&lt;li>Paul King covers &lt;a href="http://fr.slideshare.net/paulk_asert/groovy-transforms">Groovy AST transformations&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau will present &amp;ldquo;&lt;a href="https://twitter.com/cedricchampeau/status/507130051909808130">Rethinking API design with traits&lt;/a>&amp;rdquo; at JavaOne&lt;/li>
&lt;li>Andrés Almiray will &lt;a href="https://twitter.com/aalmiray/status/507263810932310016">evangelize Gradle at the Jazoon&lt;/a> conference&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/507862222585200640">Groovy 2.4 beta 3&lt;/a> available on GVM&lt;/li>
&lt;li>Søren Berg Glasius tells us that &lt;a href="https://twitter.com/sbglasius/status/507726726697218048">GroovyBlogs is coming back to life&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/507663212918349824">Gradle 2.1-rc-4&lt;/a> is available on GVM&lt;/li>
&lt;li>&lt;a href="https://twitter.com/msgilligan/status/503365901052149760">Groovy had Peter Ledbrook at &amp;ldquo;properties&amp;rdquo;&lt;/a>&lt;/li>
&lt;li>Dan Woods believes &lt;a href="https://twitter.com/danveloper/status/502900825161601024">Ratpack&amp;rsquo;s handler chain and Groovy DSL is the best combo for building restful APIs&lt;/a>&lt;/li>
&lt;li>You can &lt;a href="https://twitter.com/gvmtool/status/502358588539826176">customize the GVM self-update prompt&lt;/a>&lt;/li>
&lt;li>Ted Naleid notes that the &lt;a href="https://twitter.com/tednaleid/status/501881682178220033">@DirtiesRuntime test-level annotation can mark tests with metaclass changes in Grails&lt;/a> 2.4.4&lt;/li>
&lt;li>Even during the SpringOne2GX keynote, &lt;a href="https://twitter.com/ryanvanderwerf/status/509130411125919744">GVM makes the show&lt;/a>!&lt;/li>
&lt;li>&lt;a href="https://twitter.com/cedricchampeau/status/509350223521406976">All the new Groovy documentation is tested&lt;/a>: all snippets are included from test cases.&lt;/li>
&lt;li>Lots of Grails 3 related tweets following up Graeme Rochere’s Grails 3 preview at SpringOne2GX 2014
&lt;ul>
&lt;li>The &lt;a href="https://twitter.com/rfletcherew/status/509374166555176960">Grails 3 preview gets developers excited&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/509342714249220096">Spring Boot 1.2.0.M1&lt;/a> is available on GVM&lt;/li>
&lt;li>&lt;a href="https://twitter.com/danveloper/status/509371622143184896">Grails 3 is abandoning Gant in favor of Gradle&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/danveloper/status/509139062561599489">Goals for Grails 3&lt;/a>: support a plugin model, better eventing, better micro-services, based off Spring Boot, and more&lt;/li>
&lt;li>&lt;a href="https://twitter.com/danveloper/status/509139609997950976">Grails 3 will make a clean delineation between plugins&lt;/a>, distinguishing code generation, build-time, runtime&lt;/li>
&lt;li>&lt;a href="https://twitter.com/danveloper/status/509373452277805056">Grails 3 apps can be packaged as runnable JARs&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/danveloper/status/509382811376041986">Grails 3 will be independent of the Servlet API&lt;/a>&lt;/li>
&lt;li>You can &lt;a href="https://twitter.com/danveloper/status/509375965743431680">create micro-Grails applications that as simple Groovy scripts&lt;/a>&lt;/li>
&lt;li>Grails 3 is going to feature &lt;a href="https://twitter.com/danveloper/status/509380846319456257">application profiles&lt;/a>&lt;/li>
&lt;li>Grails 3 will allow you to &lt;a href="https://twitter.com/pledbrook/status/509380623312502784">debug your controllers&lt;/a> more easily as they are now Groovy traits&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/danveloper/status/509383407516647424">roadmap for Grails 3 is to have a first milestone before the end of the year&lt;/a> and the GA before next year&amp;rsquo;s SpringOne2GX, a year from now.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://grooscript.org/demo/bezier.html">Painting like Jackson Pollock&lt;/a>, in Groovy, in the browser, thanks to GrooScript with this demo from Jorge Franco Leza&lt;/li>
&lt;li>Graeme Rocher wowed the crowds at SpringOne2GX&amp;rsquo;s keynote with a &lt;a href="https://twitter.com/danveloper/status/509141002838220800">tweetable full data-driven rest application with Grails 3&lt;/a>&lt;/li>
&lt;li>Cédric Champeau shares a Gradle tip to &lt;a href="https://gist.github.com/melix/60d9f94a2ab961c1a0da">bypass odd VerifyErrors when you&amp;rsquo;re developing Android applications in Groovy&lt;/a>&lt;/li>
&lt;li>Lari Hotari contributed &lt;a href="https://github.com/ratpack/ratpack/pull/433">Spring Boot support for Ratpack&lt;/a>&lt;/li>
&lt;li>Schalk Cronjé shares a tip on &lt;a href="https://gist.github.com/ysb33r/68ba7ad1279d0492086a">how control logging running tests in Gradle&lt;/a>&lt;/li>
&lt;li>Stéphane Nicoll shows a neat &lt;a href="https://twitter.com/snicoll/status/507924858823786496">Spring Boot rabbit support from Groovy&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/danveloper/status/509374137622482946">Grails 3 and Spring Boot integration&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://cfp.devoxx.be/2014">Devoxx 2014 will feature Groovy&lt;/a> with Cédric Champeau speaking of Groovy on Android, and Guillaume Laforge on Groovy in the light of Java 8&lt;/li>
&lt;li>Phil Webb will be talking about &lt;a href="http://www.meetup.com/San-Francisco-Grails-Centro/events/205421502/">Spring Boot at the Groovy Grails Centro meetup on September 23rd&lt;/a>&lt;/li>
&lt;li>Guillaume Laforge is going to speak about &lt;a href="https://twitter.com/vfabric/status/509324343982424065">Groovy in the light of Java 8 at JavaOne&lt;/a> 2014&lt;/li>
&lt;/ul>
&lt;h2 id="books">Books&lt;/h2>
&lt;ul>
&lt;li>Interested in beta-testing an &lt;a href="https://twitter.com/ErinWith2Ls/status/506826876266889216">introductory Groovy and Grails book&lt;/a>?&lt;/li>
&lt;li>&lt;a href="https://twitter.com/manningbooks/status/509359104692084736">44% off of Manning’s Groovy, Grails, Spring books&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>Direct live from <a href="http://springone2gx.com/">SpringOne2GX</a>, in Dallas! So you can expect some news and presentations from the show this week and the next, and certainly tons of tweets!</p>
<p>Speaking of tweets from SpringOne2GX, you’ll notice the flurry of very positive and excited reactions regarding the upcoming Grails 3 release.</p>
<p>In the release section, don’t miss the release of Gradle 2.1.</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://forums.gradle.org/gradle/topics/gradle-2-1-released">Gradle 2.1</a> released with community plugins, and incremental java compilation</li>
<li><a href="https://twitter.com/gvmtool/status/506086805759066112">GVM 2.1</a> released with improved self-updates</li>
<li><a href="https://github.com/Arasthel/SwissKnife">Swiss Army</a>, a handy library for Groovy on Android, covering view injection, threading and more, by Jorge Martín</li>
<li><a href="http://forums.gradle.org/gradle/topics/gradle-2-1-rc-4-is-now-available-for-testing">Gradle 2.1 rc-4</a> is ready for testing</li>
<li><a href="https://spring.io/blog/2014/09/05/spring-boot-1-1-6-released">Spring Boot 1.1.6</a> released</li>
<li>A <a href="https://github.com/robertpanzer/asciidoctorj-groovy-dsl">Groovy DSL that allows for easy definition of Asciidoctor extensions</a> by Robert Panzer</li>
<li><a href="https://twitter.com/aalmiray/status/507818302496702465">Asciidoctor Gradle Template 1.0.0</a> announced by Andrés Almiray</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li><a href="http://www.groovyblogs.org/">GroovyBlogs</a> is coming back to life thanks to Søren Berg Glasius</li>
<li>Jacob Aae Mikkelsen <a href="http://grydeske.net/news/show/60">Grails Diary week 36</a></li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li><a href="http://docs.codehaus.org/display/GPARS/2014/09/06/Remoting+for+GPars">Remoting comes to GPars</a>, thanks to Google Summer of Code Rafał Sławik&rsquo;s great work</li>
<li><a href="http://kyleboon.org/blog/2014/08/14/ratpack-plus-docker-plus-gradle/">Ratpack + Docker + Gradle</a> by Kyle Boon</li>
<li>Benjamin Muschko speaks about <a href="http://www.gradleware.com/tutorials/feature-spotlight-gradles-support-maven-pom-profiles/">Gradle&rsquo;s support of Maven POM profiles</a></li>
<li>&ldquo;I don&rsquo;t like Grails / Hibernate&rdquo; goes on, covering <a href="http://rpeszek.blogspot.fr/2014/09/i-dont-like-hibernategrails-part-6-how.html">how to save objects using refresh()</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li><a href="http://www.gradleware.com/conferences/gradle-summit-2014-video-gradle-enterprise-linkedin/">Gradle in the enterprise at LinkedIn</a> at Gradle Summit 2014</li>
<li><a href="http://www.parleys.com/play/530b52fbe4b085f68c6b653f/chapter0/about">Benefit from Groovy, when, why, and how</a>, presented by Guillaume Laforge at JFokus 2014</li>
<li>The <a href="https://twitter.com/ShlomiBenHaim/status/508772445852278784">Groovy way to write Jenkins CI plugins</a> by Shiran Rubin</li>
<li><a href="http://www.infoq.com/fr/presentations/programmation-fonctionnelle-groovy">Functional programming in Groovy</a>, by Guillaume Laforge, recorded at BreizhCamp 2014 (in French)</li>
<li><a href="http://jaxenter.de/videos/funktionen-monaden-groovy-jaxtv-175429">How to get fully functional in Groovy</a>, by Dierk König (in German)</li>
<li>Video tutorial how <a href="http://www.gradleware.com/conferences/gradle-summit-2014-video-gradle-ides/">Gradle is integrated in Java IDEs</a></li>
<li>A <a href="http://www.java-tv.com/2014/08/28/groovy-introduction/">Groovy introduction presentation</a> by Joachim Baumann</li>
<li>First slide decks published from SpringOne2GX 2014 in Dallas, Texas:
<ul>
<li>Cédric Champeau dived into <a href="https://speakerdeck.com/melix/rethinking-api-design-with-traits">rethinking API design with Groovy traits</a></li>
<li>Guillaume Laforge presented the <a href="https://speakerdeck.com/glaforge/groovy-in-2014-and-beyond-at-springone2gx-2014">latest state of the Groovy nation</a> at SpringOne2GX 2014</li>
<li>Guillaume Laforge answers the question wheter <a href="https://speakerdeck.com/glaforge/groovy-in-the-light-of-java-8-springone2gx-2014">Groovy is still relevant now that we have Java 8</a></li>
<li>Paul King speaks about <a href="http://fr.slideshare.net/paulk_asert/groovy-databases">working with databases in Groovy</a></li>
<li>Paul King covers <a href="http://fr.slideshare.net/paulk_asert/groovy-transforms">Groovy AST transformations</a></li>
</ul>
</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Cédric Champeau will present &ldquo;<a href="https://twitter.com/cedricchampeau/status/507130051909808130">Rethinking API design with traits</a>&rdquo; at JavaOne</li>
<li>Andrés Almiray will <a href="https://twitter.com/aalmiray/status/507263810932310016">evangelize Gradle at the Jazoon</a> conference</li>
<li><a href="https://twitter.com/gvmtool/status/507862222585200640">Groovy 2.4 beta 3</a> available on GVM</li>
<li>Søren Berg Glasius tells us that <a href="https://twitter.com/sbglasius/status/507726726697218048">GroovyBlogs is coming back to life</a></li>
<li><a href="https://twitter.com/gvmtool/status/507663212918349824">Gradle 2.1-rc-4</a> is available on GVM</li>
<li><a href="https://twitter.com/msgilligan/status/503365901052149760">Groovy had Peter Ledbrook at &ldquo;properties&rdquo;</a></li>
<li>Dan Woods believes <a href="https://twitter.com/danveloper/status/502900825161601024">Ratpack&rsquo;s handler chain and Groovy DSL is the best combo for building restful APIs</a></li>
<li>You can <a href="https://twitter.com/gvmtool/status/502358588539826176">customize the GVM self-update prompt</a></li>
<li>Ted Naleid notes that the <a href="https://twitter.com/tednaleid/status/501881682178220033">@DirtiesRuntime test-level annotation can mark tests with metaclass changes in Grails</a> 2.4.4</li>
<li>Even during the SpringOne2GX keynote, <a href="https://twitter.com/ryanvanderwerf/status/509130411125919744">GVM makes the show</a>!</li>
<li><a href="https://twitter.com/cedricchampeau/status/509350223521406976">All the new Groovy documentation is tested</a>: all snippets are included from test cases.</li>
<li>Lots of Grails 3 related tweets following up Graeme Rochere’s Grails 3 preview at SpringOne2GX 2014
<ul>
<li>The <a href="https://twitter.com/rfletcherew/status/509374166555176960">Grails 3 preview gets developers excited</a></li>
<li><a href="https://twitter.com/gvmtool/status/509342714249220096">Spring Boot 1.2.0.M1</a> is available on GVM</li>
<li><a href="https://twitter.com/danveloper/status/509371622143184896">Grails 3 is abandoning Gant in favor of Gradle</a></li>
<li><a href="https://twitter.com/danveloper/status/509139062561599489">Goals for Grails 3</a>: support a plugin model, better eventing, better micro-services, based off Spring Boot, and more</li>
<li><a href="https://twitter.com/danveloper/status/509139609997950976">Grails 3 will make a clean delineation between plugins</a>, distinguishing code generation, build-time, runtime</li>
<li><a href="https://twitter.com/danveloper/status/509373452277805056">Grails 3 apps can be packaged as runnable JARs</a></li>
<li><a href="https://twitter.com/danveloper/status/509382811376041986">Grails 3 will be independent of the Servlet API</a></li>
<li>You can <a href="https://twitter.com/danveloper/status/509375965743431680">create micro-Grails applications that as simple Groovy scripts</a></li>
<li>Grails 3 is going to feature <a href="https://twitter.com/danveloper/status/509380846319456257">application profiles</a></li>
<li>Grails 3 will allow you to <a href="https://twitter.com/pledbrook/status/509380623312502784">debug your controllers</a> more easily as they are now Groovy traits</li>
<li>The <a href="https://twitter.com/danveloper/status/509383407516647424">roadmap for Grails 3 is to have a first milestone before the end of the year</a> and the GA before next year&rsquo;s SpringOne2GX, a year from now.</li>
</ul>
</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li><a href="http://grooscript.org/demo/bezier.html">Painting like Jackson Pollock</a>, in Groovy, in the browser, thanks to GrooScript with this demo from Jorge Franco Leza</li>
<li>Graeme Rocher wowed the crowds at SpringOne2GX&rsquo;s keynote with a <a href="https://twitter.com/danveloper/status/509141002838220800">tweetable full data-driven rest application with Grails 3</a></li>
<li>Cédric Champeau shares a Gradle tip to <a href="https://gist.github.com/melix/60d9f94a2ab961c1a0da">bypass odd VerifyErrors when you&rsquo;re developing Android applications in Groovy</a></li>
<li>Lari Hotari contributed <a href="https://github.com/ratpack/ratpack/pull/433">Spring Boot support for Ratpack</a></li>
<li>Schalk Cronjé shares a tip on <a href="https://gist.github.com/ysb33r/68ba7ad1279d0492086a">how control logging running tests in Gradle</a></li>
<li>Stéphane Nicoll shows a neat <a href="https://twitter.com/snicoll/status/507924858823786496">Spring Boot rabbit support from Groovy</a></li>
<li><a href="https://twitter.com/danveloper/status/509374137622482946">Grails 3 and Spring Boot integration</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="http://cfp.devoxx.be/2014">Devoxx 2014 will feature Groovy</a> with Cédric Champeau speaking of Groovy on Android, and Guillaume Laforge on Groovy in the light of Java 8</li>
<li>Phil Webb will be talking about <a href="http://www.meetup.com/San-Francisco-Grails-Centro/events/205421502/">Spring Boot at the Groovy Grails Centro meetup on September 23rd</a></li>
<li>Guillaume Laforge is going to speak about <a href="https://twitter.com/vfabric/status/509324343982424065">Groovy in the light of Java 8 at JavaOne</a> 2014</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>Interested in beta-testing an <a href="https://twitter.com/ErinWith2Ls/status/506826876266889216">introductory Groovy and Grails book</a>?</li>
<li><a href="https://twitter.com/manningbooks/status/509359104692084736">44% off of Manning’s Groovy, Grails, Spring books</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Beta 3 for Groovy 2.4</title><link>https://glaforge.dev/posts/2014/09/02/beta-3-for-groovy-2-4/</link><pubDate>Tue, 02 Sep 2014 02:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/09/02/beta-3-for-groovy-2-4/</guid><description>&lt;p>The Groovy development team is happy to announce the release of a third beta for Groovy 2.4.&lt;/p>
&lt;p>This release contains various bug fixes and minor improvements.&lt;/p>
&lt;p>I&amp;rsquo;ll highlight the fact that you can now use multiple labels on your statements (instead of just one).&lt;/p>
&lt;p>With beta-3 now, many of the unneeded synthetic methods generated by Groovy are not generated anymore, reducing the number of methods nicely yielding smaller bytecode. This is of particular interest for Android developers limited in the overall number of methods used in Android applications.&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy development team is happy to announce the release of a third beta for Groovy 2.4.</p>
<p>This release contains various bug fixes and minor improvements.</p>
<p>I&rsquo;ll highlight the fact that you can now use multiple labels on your statements (instead of just one).</p>
<p>With beta-3 now, many of the unneeded synthetic methods generated by Groovy are not generated anymore, reducing the number of methods nicely yielding smaller bytecode. This is of particular interest for Android developers limited in the overall number of methods used in Android applications.</p>
<p>You can read the <a href="https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=20544">JIRA release notes</a> and go straight to the <a href="http://beta.groovy-lang.org/download.html">download area</a>!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #34 back from vacations!</title><link>https://glaforge.dev/posts/2014/09/02/groovy-weekly-34-back-from-vacations/</link><pubDate>Tue, 02 Sep 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/09/02/groovy-weekly-34-back-from-vacations/</guid><description>&lt;p>After a long summer break, it’s high time we resume Groovy Weekly, and come back to our every-Tuesday agenda! Get ready for lots of content!&lt;/p>
&lt;p>If there were one particular item of news I’d like to highlight in this edition, that’s the news about the &lt;a href="http://open.blogs.nytimes.com/2014/08/18/getting-groovy-with-reactive-android/?_php=true&amp;amp;_type=blogs&amp;amp;_r=0">New York Times that is going to use Groovy and its Android support&lt;/a> (in the upcoming Groovy 2.4), as well as RxJava, to revamp its Android application to make more “reactive”.&lt;/p></description><content:encoded>
<![CDATA[<p>After a long summer break, it’s high time we resume Groovy Weekly, and come back to our every-Tuesday agenda! Get ready for lots of content!</p>
<p>If there were one particular item of news I’d like to highlight in this edition, that’s the news about the <a href="http://open.blogs.nytimes.com/2014/08/18/getting-groovy-with-reactive-android/?_php=true&amp;_type=blogs&amp;_r=0">New York Times that is going to use Groovy and its Android support</a> (in the upcoming Groovy 2.4), as well as RxJava, to revamp its Android application to make more “reactive”.</p>
<p>Speaking of Groovy 2.4, it’s interesting to note that the new beta released today reduces the bytecode size, by not generating unneeded methods anymore. This should make Android developers happy, since they are overal limited in the number of methods in Android applications.</p>
<p>Next week will be pretty Groovy in Dallas, as the SpringOne2GX conference will be on. Perhaps we won’t have yet much news and presentations to share next Tuesday, but the following week will list all the great content and news happening at the conference. If you’re around, don’t hesitate to tell hi!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://glaforge.dev/posts/2014/09/02/beta-3-for-groovy-2-4/">Groovy 2.4-beta-3</a> released with reduced synthetic methods (particularly handy for Android developers) to reduce the bytecode size</li>
<li><a href="http://www.ratpack.io/versions/0.9.8">Ratpack 0.9.8</a> released with Ratpack&rsquo;s HttpClient testing support, dangling request detection, and reactive stream support</li>
<li><a href="http://docs.codehaus.org/display/GRIFFON/2014/08/11/Griffon+2.0.0.RC2+Released">Griffon 2.0 RC-2</a> released</li>
<li><a href="http://forums.gradle.org/gradle/topics/gradle-2-1-rc-3-is-now-available-for-testing">Gradle 2.1 RC-3</a> is available for testing</li>
<li><a href="https://bintray.com/chiquitinxx/grooscript/org.grooscript%3Agrooscript-gradle-plugin/0.5.1">Grooscript Gradle plugin 0.5.1</a> is out</li>
<li><a href="https://github.com/grails-samples/grails-petclinic">Grails Petclinic sample</a> app has been updated to 2.4.3 and now uses the asset-pipeline plugin and has new tests for the Domain classes.</li>
<li><a href="http://www.gdub.rocks/">Gdub, an enhanced Gradle wrapper</a></li>
<li><a href="https://code.google.com/p/jlabgroovy/wiki/JavaFXChartsInGroovyLab">JavaFX charting support for GroovyLab</a></li>
<li>Fabiano Taioli created a <a href="http://fbn.github.io/gimple/">micro Groovy, thread-safe, dependency injection container</a>, called Gimple</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>The <a href="http://open.blogs.nytimes.com/2014/08/18/getting-groovy-with-reactive-android/?_php=true&amp;_type=blogs&amp;_r=0">New York Times is going Groovy with Reactive Android</a> by Mohit Panday</li>
<li>The Groovy project on Github has just crossed the <a href="https://github.com/groovy/groovy-core/pulls">500th pull requests</a> mark. Still some work to close the 25 still open though! Keep&rsquo;em coming, it&rsquo;s a wonderful way to contribute to the project!</li>
<li>The Grails Diary by Jacob Aae Mikkelsen
<ul>
<li><a href="http://grydeske.net/news/show/56">Week 32</a></li>
<li><a href="http://grydeske.net/news/show/57">Week 33</a></li>
<li><a href="http://grydeske.net/news/show/58">Week 34</a></li>
<li><a href="http://grydeske.net/news/show/59">Week 35</a></li>
</ul>
</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li><a href="http://www.objectpartners.com/2014/08/25/gr8conf-us-recap-why-your-company-should-adopt-groovy/">Why your company should be using Groovy</a>? Scott Hickey comes back on the Mutual of Omaha Groovy case study</li>
<li>Mark Perry on <a href="http://mperry.github.io/2014/08/19/groovy-monads.html">Groovy Monads</a></li>
<li><a href="http://grooscript.org/nodejs_example.html">Using the GrooScript Node.js support</a></li>
<li>Robert Fletcher uses <a href="http://blog.freeside.co/2014/08/07/spock-and-hamcrest/">Hamcrest with Spock</a></li>
<li><a href="http://blog.freeside.co/2014/08/11/multiple-interface-mocks-with-spock/">Multiple interface mocks with Spock</a> by Rob Fletcher</li>
<li><a href="http://swalsh.org/blog/2014/08/26/ratpack-first-impressions/">Ratpack: First Impressions</a>, by Sean Walsh</li>
<li>Andrés Almiray&rsquo;s Gradle glam: <a href="http://www.jroller.com/aalmiray/entry/gradle_glam_custom_asciidoctor_extensions">custom Asciidoctor extensions</a> in Groovy</li>
<li>MrHaki’s Asciidoctor goodness: <a href="http://mrhaki.blogspot.fr/2014/08/awesome-asciidoc-write-extensions-using.html">writing extensions in Groovy</a></li>
<li>MrHaki&rsquo;s Groovy goodness
<ul>
<li>use <a href="http://mrhaki.blogspot.fr/2014/08/groovy-goodness-use-custom-template.html">custom template class with MarkupTemplateEngine</a></li>
<li><a href="http://mrhaki.blogspot.fr/2014/08/groovy-goodness-nested-templates-with.html">nested templates with MarkupTemplateEngine</a></li>
<li><a href="http://mrhaki.blogspot.fr/2014/08/groovy-goodness-using-layouts-with.html">using layouts with MarkupTemplateEngine</a></li>
</ul>
</li>
<li>MrHaki&rsquo;s Gradle goodness
<ul>
<li><a href="http://mrhaki.blogspot.fr/2014/08/gradle-goodness-getting-more-dependency.html">getting more dependency insight</a></li>
<li><a href="http://mrhaki.blogspot.fr/2014/08/gradle-goodness-suppress-progress.html">suppress progress logging</a></li>
<li><a href="http://www.cholick.com/entry/show/280">Eliminating development redeploys</a> using Gradle</li>
</ul>
</li>
<li>Romin Irani’s Gradle tutorial series
<ul>
<li>Part 4: <a href="http://rominirani.com/2014/08/12/gradle-tutorial-part-4-java-web-applications/">Java web applications</a></li>
<li>Part 5: <a href="http://rominirani.com/2014/08/15/gradle-tutorial-part-5-gradle-app-engine-plugin/">Google AppEngine plugin</a></li>
<li>Part 6: <a href="http://rominirani.com/2014/08/19/gradle-tutorial-part-6-android-studio-gradle/">Android Studio and Gradle</a></li>
<li>Part 7: <a href="http://rominirani.com/2014/08/20/gradle-tutorial-part-7-android-studio-app-engine-gradle/">Android Studio, Gradle and Google AppEngine</a></li>
<li>Part 8: <a href="http://rominirani.com/2014/08/25/gradle-tutorial-part-8-gradle-app-engine-endpoints-android-studio/">Gradle, Google AppEngine, Endpoints and Android Studio</a></li>
<li>Part 9: <a href="http://rominirani.com/2014/08/26/gradle-tutorial-part-9-cloud-endpoints-persistence-android-studio/">Cloud Endpoints, persistence and Android Studio</a></li>
<li>Part 10: <a href="http://rominirani.com/2014/08/27/gradle-tutorial-part-10-consuming-endpoints-in-android-code/">Consuming Cloud Endpoints in Android Studio</a></li>
</ul>
</li>
<li>Robert Peszek&rsquo;s Grails/Hibernate series:
<ul>
<li>Part 3: <a href="http://rpeszek.blogspot.fr/2014/08/i-dont-like-grailshibernate-part-3.html">DuplicateKeyException, catch it if you can</a></li>
<li>Part 4: <a href="http://rpeszek.blogspot.fr/2014/08/i-dont-like-grailshibernat-part-4.html">Hibernate proxy objects</a></li>
<li>Part 5: <a href="http://rpeszek.blogspot.fr/2014/08/i-dont-like-hibernategrails-part-5-auto.html">auto-saving and auto-flushing</a></li>
</ul>
</li>
<li><a href="http://www.objectpartners.com/2014/08/19/add-javascript-unit-tests-and-run-them-with-grails-test-app/">Add Javascript unit tests and run them with “grails test-app”</a> by Igor Shults</li>
<li>Dmitriy Drenkalyuk covers <a href="http://sysgears.com/articles/advanced-gorm-features-inheritance-embedded-data-maps-and-lists-storing/">advanced GORM features</a>: inheritance, embedded data, maps and lists storing</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Venkat Subramaniam reveals <a href="https://twitter.com/agilelearner/status/496970094946562048">how Groovy named parameter constructor works</a></li>
<li>GR8Conf US 2014 videos
<ul>
<li><a href="https://www.youtube.com/watch?v=KvBjO4WXL24&amp;feature=youtu.be">DevOps, Chef and Grails</a>, presented by Ken Liu</li>
<li><a href="https://www.youtube.com/watch?v=k6vXQwxk7N8&amp;feature=youtu.be">Groovy Puzzlers</a> presentation by Baruch Sadogursky and Andrés Almiray</li>
<li><a href="https://www.youtube.com/watch?v=oF-gK-x8RGw&amp;feature=youtu.be">Why your build matters</a>, by Peter Ledbrook</li>
<li><a href="https://www.youtube.com/watch?v=ROYQlc-wVEg">One build to rule them all</a>, by John Engelman</li>
<li><a href="https://www.youtube.com/watch?v=bTRUC78X87g">REPL driven development with the Grails console</a> plugin, by David Kuster</li>
</ul>
</li>
<li>A paid video on <a href="https://www.packtpub.com/effective-gradle-implementation/video">effective Gradle implementation</a> by Lee Fox and Ryan Vanderwerf for Packt Publishing</li>
</ul>
<h2 id="google-posts">Google+ posts</h2>
<ul>
<li>Richard Vowles releases a <a href="https://plus.google.com/u/0/+RichardVowles/posts/fQzj2Zfn8Ux">new version of his Maven Groovydoc plugin</a></li>
<li>Al Baker reports about <a href="https://plus.google.com/b/113675159854671799959/+AlBakerDev/posts/11DxyvNoKi7?cfem=1">Groovy&rsquo;s presence the next semantic web conference</a> with GroovySparql and Stardog-Groovy</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>GVM 2.0 is rolling out a <a href="https://twitter.com/gvmtool/status/497521264094093313">new broadcast API</a></li>
<li><a href="https://twitter.com/gvmtool/status/497490734980005889">Spring Boot 1.1.5</a> is available through GVM</li>
<li><a href="https://twitter.com/gvmtool/status/505331657508134912">Gradle 2.1 RC-2</a> is available on GVM</li>
<li>The <a href="https://twitter.com/pledbrook/status/503823613536251904">Lazybones Gradle plugin</a> is now in the Gradle plugin portal</li>
<li><a href="https://twitter.com/gvmtool/status/503591205071233024">Lazybones 0.7.1</a> is on GVM</li>
<li>Cédric Champeau is <a href="https://twitter.com/cedricchampeau/status/503834637203173378">gathering feedback about Groovy traits in production</a> for his SpringOne2GX presentation</li>
<li>Cédric Champeau will be speaking about <a href="https://twitter.com/cedricchampeau/status/504599269379436545">Groovy and Android at Devoxx</a></li>
<li>The Asciidoctor Java integration <a href="https://twitter.com/ysb33r/status/504665335476539392">(Asciidoctorj) is on the road to Gradle</a></li>
<li><a href="https://twitter.com/cedricchampeau/status/506465145825267712">Marcin Erdmann recently joined the GradleWare</a> team. Congrats to him!</li>
<li>Peter Ledbrook reminds us we can <a href="https://twitter.com/pledbrook/status/505744842568531968">submit talks for the Groovy / Grails eXchange</a> conference in London, next December</li>
</ul>
<h2 id="mailing-lists-posts">Mailing-lists posts</h2>
<ul>
<li>The debate around advocating Groovy vs Java resumes speaking about <a href="http://groovy.329449.n5.nabble.com/Advocating-Groovy-vs-Java-Phase-II-How-to-Manage-Groovy-Adoption-td5720932.html">how to manage adoption</a></li>
<li>A discussion on <a href="http://groovy.329449.n5.nabble.com/Using-groovy-in-a-JEE-project-td5720948.html">using Groovy in a Java EE project</a></li>
<li>Søren Berg Glasius is planning on taking over and <a href="http://groovy.329449.n5.nabble.com/GroovyBlogs-td5721104.html">updating the GroovyBlogs website</a></li>
<li><a href="http://groovy.329449.n5.nabble.com/The-potential-of-Groovy-for-unit-testing-Java-code-The-quot-ticket-to-Groovy-quot-for-millions-of-Ja-td5720984.html">Groovy as a gateway drug into the enterprise</a></li>
<li>Sergei Egorov plays with <a href="http://groovy.329449.n5.nabble.com/Basic-Scala-like-pattern-matching-implementation-td5721078.html">pattern matching in Groovy</a> thanks to his Groovy Macro project</li>
<li>Sergei Egorov created an <a href="http://groovy.329449.n5.nabble.com/Online-Groovy-AST-Console-td5720986.html">online Groovy AST console</a></li>
<li>Recent <a href="http://groovy.329449.n5.nabble.com/Oracle-breaking-Groovy-is-a-problem-td5720928.html">Oracle&rsquo;s Java 8 updates have been breaking Groovy</a> but the Groovy team works with Oracle to resolve those problems</li>
<li>Ways to get an <a href="http://groovy.329449.n5.nabble.com/groovy-in-jar-td5720858.html">executable Groovy program to run without Groovy installed</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Following up a question on the Groovy mailing-list about <a href="https://github.com/glaforge/disable-grab-sample/blob/master/src/main/groovy/disablegrab/DisableGrabTransformation.groovy">how to disable @Grab</a>, Guillaume Laforge created a small sample project showing a global AST transformation that generates compilation errors when the @Grab or @Grapes annotations are used in a Groovy script</li>
<li>A <a href="http://thedailywtf.com/Articles/Securing-Input.aspx">Groovy puzzler in the &ldquo;daily WTF&rdquo;</a> about input validation</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="https://twitter.com/ben_t_mcguire/status/503631937701830658">First San Diego Groovy user group meeting</a> on September 3rd</li>
<li>The <a href="https://twitter.com/skillsmatter/status/496936164809793536">Groovy &amp; Grails eXchange Call for Paper is already open</a>!</li>
<li>A <a href="https://skillsmatter.com/courses/265-groovy-and-grails-workshop">Groovy and Grails training</a> by Peter Ledbrook in London, end of September</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li><a href="http://www.amazon.com/gp/product/1617290963">Grails in Action 2nd edition</a> is now available on Amazon</li>
<li>MrHaki&rsquo;s <a href="http://mrhaki.blogspot.fr/2014/08/grails-goodness-notebook-updated.html">Grails Goodness notebook has been updated</a> with new recipes</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Disable @Grab with a global AST transformation</title><link>https://glaforge.dev/posts/2014/08/07/disable-grab-with-a-global-ast-transformation/</link><pubDate>Thu, 07 Aug 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/08/07/disable-grab-with-a-global-ast-transformation/</guid><description>&lt;p>On the Groovy mailing-list, we had an interesting question about &lt;a href="http://groovy.329449.n5.nabble.com/Disabling-Grape-td5720694.html">how to disable annotations like @Grab&lt;/a>, to prevent users from downloading third-party dependencies. There are a few possibilities for that, but my favorite was to create a global AST transformation that would generate a compilation error if the @Grab annotation is found on an import.&lt;/p>
&lt;p>I created a first &lt;a href="http://groovyconsole.appspot.com/script/5686306919153664">small prototype within a script&lt;/a>, but I used an injected local transformation to get everything working with a simple script. So I decided afterwards to do it for real this time, using a real &lt;a href="https://github.com/glaforge/disable-grab-sample/">project on Github&lt;/a> with a proper global AST transformation this time.&lt;/p></description><content:encoded>
<![CDATA[<p>On the Groovy mailing-list, we had an interesting question about <a href="http://groovy.329449.n5.nabble.com/Disabling-Grape-td5720694.html">how to disable annotations like @Grab</a>, to prevent users from downloading third-party dependencies. There are a few possibilities for that, but my favorite was to create a global AST transformation that would generate a compilation error if the @Grab annotation is found on an import.</p>
<p>I created a first <a href="http://groovyconsole.appspot.com/script/5686306919153664">small prototype within a script</a>, but I used an injected local transformation to get everything working with a simple script. So I decided afterwards to do it for real this time, using a real <a href="https://github.com/glaforge/disable-grab-sample/">project on Github</a> with a proper global AST transformation this time.</p>
<p>If you checkout the project from Github, with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git clone git@github.com:glaforge/disable-grab-sample.git
</span></span></code></pre></div><p>You then &lsquo;cd&rsquo; in the disable-grab-sample directory, and you can run the following command to launch the Spock test showing the transformation in action:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>./gradlew <span style="color:#007020">test</span>
</span></span></code></pre></div><p>So what&rsquo;s inside that project? First of all, we need to create our AST transformation (I&rsquo;ll skip the imports for brevity):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@GroovyASTTransformation</span><span style="color:#666">(</span>phase <span style="color:#666">=</span> CompilePhase<span style="color:#666">.</span><span style="color:#4070a0">CANONICALIZATION</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">DisableGrabTransformation</span> <span style="color:#007020;font-weight:bold">implements</span> ASTTransformation <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">void</span> <span style="color:#06287e">visit</span><span style="color:#666">(</span>ASTNode<span style="color:#666">[]</span> nodes<span style="color:#666">,</span> SourceUnit source<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#902000">def</span> imports <span style="color:#666">=</span> source<span style="color:#666">.</span><span style="color:#4070a0">AST</span><span style="color:#666">.</span><span style="color:#4070a0">imports</span>
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>imports<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>            imports<span style="color:#666">.</span><span style="color:#4070a0">each</span> <span style="color:#666">{</span> anImport <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>                anImport<span style="color:#666">.</span><span style="color:#4070a0">annotations</span><span style="color:#666">.</span><span style="color:#4070a0">each</span> <span style="color:#666">{</span> anno <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>anno<span style="color:#666">.</span><span style="color:#4070a0">classNode</span><span style="color:#666">.</span><span style="color:#4070a0">name</span> <span style="color:#007020;font-weight:bold">in</span> <span style="color:#666">[</span><span style="color:#4070a0">&#39;groovy.lang.Grab&#39;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#39;groovy.lang.Grapes&#39;</span><span style="color:#666">])</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                        source<span style="color:#666">.</span><span style="color:#4070a0">errorCollector</span><span style="color:#666">.</span><span style="color:#4070a0">addError</span><span style="color:#666">(</span>
</span></span><span style="display:flex;"><span>                            <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">SyntaxErrorMessage</span><span style="color:#666">(</span>
</span></span><span style="display:flex;"><span>                                <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">SyntaxException</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;@Grab and @Grapes are forbidden&#39;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>                                anImport<span style="color:#666">.</span><span style="color:#4070a0">lineNumber</span><span style="color:#666">,</span> anImport<span style="color:#666">.</span><span style="color:#4070a0">columnNumber</span><span style="color:#666">),</span> 
</span></span><span style="display:flex;"><span>                            source<span style="color:#666">))</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>                <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>            <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>We create a class implementing the ASTTransformation class with its visit() method. In that method, we access the &ldquo;module&rdquo; imports, and if there are any import, we iterate over each of them, checking if there are annotations put on them. If those imports are annotated with @Grab or @Grapes (ie. if the annotation class node&rsquo;s fully qualified class name are the FQN of the Grab and Grapes annotations), we then use the error collector to add a new syntax error, so that a compilation error is thrown by the Groovy compiler if ever someone uses @Grab in a Groovy script or class.</p>
<p>We need to wire in that <a href="http://groovy.codehaus.org/Global+AST+Transformations">global transformation</a>. As they are not triggered by annotations like local transformations, we need do declare the transformation in a specific META-INF / services / org.codehaus.groovy.transform.ASTTransformation file, that will just contain one line: the fully qualified class name of the AST transformation that needs to be applied to each script and classes that will be compiled when this transformation is on the classpath. So our services file will just contain:</p>
<pre tabindex="0"><code>disablegrab.DisableGrabTransformation
</code></pre><p>Now we need to see if our transformation is applied, and works as expected. For that purpose, we&rsquo;ll create a <a href="http://spock-framework.readthedocs.org/en/latest/">Spock</a> test:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">DisableGrabSpec</span> <span style="color:#007020;font-weight:bold">extends</span> Specification <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> <span style="color:#4070a0">&#34;test&#34;</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#002070;font-weight:bold">given:</span>
</span></span><span style="display:flex;"><span>        <span style="color:#902000">def</span> shell <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> GroovyShell<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>        <span style="color:#002070;font-weight:bold">when:</span>
</span></span><span style="display:flex;"><span>        shell<span style="color:#666">.</span><span style="color:#4070a0">evaluate</span> <span style="color:#4070a0">&#39;&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            @Grab(&#39;org.apache.commons:commons-lang3:3.3.2&#39;)
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            import org.apache.commons.lang3.StringUtils
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">            println &#34;hi&#34;        
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">        &#39;&#39;&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#002070;font-weight:bold">then:</span>
</span></span><span style="display:flex;"><span>        <span style="color:#902000">def</span> e <span style="color:#666">=</span> thrown<span style="color:#666">(</span>MultipleCompilationErrorsException<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        e<span style="color:#666">.</span><span style="color:#4070a0">message</span><span style="color:#666">.</span><span style="color:#4070a0">contains</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;@Grab and @Grapes are forbidden&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>We are using the GroovyShell class to evaluate (and thus compile) a script that contains a @Grab instruction. When evaluating that script, a compilation error should be thrown, and we indeed assert that it&rsquo;s the case, and that our compilation error contains the message we&rsquo;ve crafted.</p>
<p>Done!</p>
<p>Let&rsquo;s step back a little with a couple words about our build file:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>apply <span style="color:#002070;font-weight:bold">plugin:</span> <span style="color:#4070a0">&#39;groovy&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>repositories <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    jcenter<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>dependencies <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    compile <span style="color:#4070a0">&#39;org.codehaus.groovy:groovy-all:2.3.6&#39;</span>
</span></span><span style="display:flex;"><span>    testCompile <span style="color:#4070a0">&#39;org.spockframework:spock-core:0.7-groovy-2.0&#39;</span>
</span></span><span style="display:flex;"><span>    testCompile <span style="color:#4070a0">&#39;org.apache.ivy:ivy:2.4.0-rc1&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Not much to see here actually! We&rsquo;re just applying the groovy plugin, use the <a href="https://bintray.com/bintray/jcenter">jcenter</a> repository from Bintray. We&rsquo;re using Groovy-all, the Spock library for our test scope, as well as the Ivy library that&rsquo;s needed by the grape infrastructure for fully functioning, for retrieving artifacts.</p>
<p>With our Gradle build file, we can call the jar task to create a JAR that will contain the META-INF/services file, and as soon as you&rsquo;ll have that JAR on your classpath with that AST transformation, any script or class compiled with it will get compilation errors if @Grab is used.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #33</title><link>https://glaforge.dev/posts/2014/08/05/groovy-weekly-33/</link><pubDate>Tue, 05 Aug 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/08/05/groovy-weekly-33/</guid><description>&lt;p>You probably remember Groovy and Gradle being selected in RebelLabs’ report as part of the 10 “kick-ass” technologies developers love? But I’m also happy to report that &lt;a href="https://glaforge.dev/posts/2014/08/01/groovy-receives-geek-choice-award/">Groovy won the Geek Choice Award&lt;/a>, showing how groovy Groovy is!&lt;/p>
&lt;p>GR8Conf US 2014 last week is delivering tons of &lt;a href="https://www.youtube.com/channel/UC7wUp2KIa1hoMNn0r7JUVEg">great videos and materials&lt;/a> from all the speakers that came to the conference, and I hope you’ll find time to watch a few. My personal highlight was the great Q&amp;amp;A talk by Scott Hickey and Jim McGill on looking back into &lt;a href="https://www.youtube.com/watch?v=CvpxRoLEq7M">9 years of using Groovy at a big US insurance company&lt;/a>. Don’t miss it!&lt;/p></description><content:encoded>
<![CDATA[<p>You probably remember Groovy and Gradle being selected in RebelLabs’ report as part of the 10 “kick-ass” technologies developers love? But I’m also happy to report that <a href="https://glaforge.dev/posts/2014/08/01/groovy-receives-geek-choice-award/">Groovy won the Geek Choice Award</a>, showing how groovy Groovy is!</p>
<p>GR8Conf US 2014 last week is delivering tons of <a href="https://www.youtube.com/channel/UC7wUp2KIa1hoMNn0r7JUVEg">great videos and materials</a> from all the speakers that came to the conference, and I hope you’ll find time to watch a few. My personal highlight was the great Q&amp;A talk by Scott Hickey and Jim McGill on looking back into <a href="https://www.youtube.com/watch?v=CvpxRoLEq7M">9 years of using Groovy at a big US insurance company</a>. Don’t miss it!</p>
<p>Also, if you missed GR8Conf US 2014, remember there’s <a href="http://springone2gx.com/">SpringOne2GX</a> fast approaching, and the <a href="https://2014.event.springone2gx.com/register">early bird price for registering to the conference ends August 9th</a>, so be quick!</p>
<p>Last but not least, the Groovy Weekly schedule will slow down a little this month, as I’ll take some vacations! So I might be skipping a beat or two in the agenda, but fear not, Groovy Weekly will come back in full force in September!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://grails.org/2.4.3+Release+Notes">Grails 2.4.3</a> is out</li>
<li><a href="http://www.ratpack.io/versions/0.9.7">Ratpack 0.9.7</a> released, with the new Groovy markup template engine, continued work on Reactive Streams, usage of the Gradle Shadow plugin</li>
<li>Renato Athaydes is releasing <a href="https://groups.google.com/forum/#!msg/spockframework/RzXOqfw4OD0/ZoJsL8VTnmgJ">v1.2 of the Spock reports</a> project</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li><a href="https://glaforge.dev/posts/2014/08/01/groovy-receives-geek-choice-award/">Groovy wins Geek Choice Award</a>, following up RebelLabs study of the 10 kick-ass technologies developers love</li>
<li>Jacob Aae Mikkelsen published <a href="http://grydeske.net/news/show/55">week 31 of the Grails Diary</a></li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>A tutorial for a <a href="http://wordpress.transentia.com.au/wordpress/2014/07/04/livin-on-the-grid/">REST / grid-driven application with Ratpack, Angular.JS and CoffeeScript</a>, as well as a comparison with Grails</li>
<li>A <a href="http://grooscript.org/ratpack-demo.html">demo using Groovy, Ratpack, the Gradle plugin, and GrooScript</a></li>
<li>Robert Peszek writes that <a href="http://rpeszek.blogspot.fr/2014/08/i-dont-like-hibernate-and-grails-part-1.html">he doesn&rsquo;t like Hibernate and Grails</a> in part one</li>
<li>Robert Peszek continues in part two with the <a href="http://rpeszek.blogspot.fr/2014/08/i-dont-like-hibernategrails-part-2.html">problems he encounters with Hibernate and Grails</a></li>
<li>Getting started guide with <a href="http://app42paas.shephertz.com/dev-center/grails-platform-as-a-service/">Grails on App42 platform as a service</a></li>
<li>Andrés Almiray comes back on <a href="http://www.jroller.com/aalmiray/entry/gradle_glam_jacoco_coveralls_take">Jacoco and Coveralls in his Gradle Glam series</a></li>
<li>MrHaki&rsquo;s Groovy goodness: <a href="http://mrhaki.blogspot.fr/2014/08/groovy-goodness-relax-groovy-will-parse.html">Relax&hellip; Groovy Will Parse Your Wicked JSON</a></li>
<li>MrHaki’s Grails goodness:
<ul>
<li><a href="http://mrhaki.blogspot.fr/2014/08/grails-goodness-use-spring-java.html">use Spring Java configuration</a></li>
<li><a href="http://mrhaki.blogspot.fr/2014/08/grails-goodness-using-bintray-jcenter.html">using Bintray JCenter as repository</a></li>
<li><a href="http://mrhaki.blogspot.fr/2014/08/grails-goodness-conditionally-load.html">load beans in Java configuration based on Grails environment</a></li>
</ul>
</li>
<li>JaxEnter comes back on the <a href="http://jaxenter.com/gradle-2-0-is-here-sleek-speedy-and-supporting-java-8-50707.html">Gradle 2 release and the interview they made with Guillaume Laforge</a> at the JAX conference</li>
<li><a href="http://www.objectpartners.com/2014/07/29/static-website-generation-in-groovy/">Static site generation with Grain</a> by Mike Hostetler</li>
<li><a href="http://www.oodlestechnologies.com/blogs/Metaobject-Protocol-%26-Interceptors-In-Grails">Meta-Object Protocol and interceptors In Grails</a> by Jasgeet Singh</li>
<li><a href="http://www.intelligrape.com/blog/2014/08/04/grails-unit-test-filters-with-the-injected-service/">Grails unit test filters with the injected service</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Julien Viet on <a href="http://www.youtube.com/watch?v=ZRQwZfP2uIc&amp;feature=youtu.be&amp;a">CRaSH</a>, the shell for the Java Virtual Machine, recorded at GR8Conf Europe 2014</li>
<li>GR8Conf US 2014 specials
<ul>
<li>The <a href="https://twitter.com/gr8confus/status/494856150261567489">GR8Conf US 2014 videos</a> will appear progressively on YouTube and the slides on Github</li>
<li>A look back at the <a href="https://www.youtube.com/watch?v=CvpxRoLEq7M">9 years of Groovy at the Mutual of Omaha insurance company</a> by Scott Hickey and Jim McGill</li>
<li>The now famous <a href="http://fr.slideshare.net/GroovyPuzzlers/the-groovy-puzzlers-gr8-conf-us-2014">Groovy Puzzlers</a> talk by Baruch Sadogursky and Andrés Almiray</li>
<li>Guillaume Laforge published his slide deck about &ldquo;<a href="https://speakerdeck.com/glaforge/groovy-in-2014-and-beyond-at-gr8conf-us-2014">Groovy in 2014 and beyond</a>&rdquo;</li>
<li>The <a href="https://www.youtube.com/watch?v=A17geQa5WQc">Grails asset pipeline</a> by Ted Naleid</li>
<li><a href="https://www.youtube.com/watch?v=fhGRLFLamN8">Groovy Vampires: Groovy, REST, NoSQL</a> and Bad Marketing by Ken Kousen</li>
<li><a href="https://www.youtube.com/watch?v=wF8gjfh0Kyw">Experiences using Grails in a micro-service architecture</a> by Jeff Beck</li>
<li><a href="https://www.youtube.com/watch?v=_cdb7zYNEFg&amp;feature=youtu.be">Hibernate metrics: paying attention to the ORM behind the curtain</a> by David Kuster</li>
<li><a href="https://www.youtube.com/watch?v=i28F13zZwlg&amp;feature=youtu.be">Spock, a logical framework for enterprise testing</a> by Ken Kousen</li>
<li><a href="https://www.youtube.com/watch?v=VvTxOwYsj88">Functional testing your Grails app with Geb</a> by Collin Harrington</li>
<li><a href="https://www.youtube.com/watch?v=-WQGIZ90H2w&amp;list=UU7wUp2KIa1hoMNn0r7JUVEg">Vagrant</a> by Kyle Boon at GR8Conf US 2014</li>
<li><a href="https://www.youtube.com/watch?v=8W8F9zVmwDs">Introduction to graph databases and Neo4J</a> by Stefan Armbruster</li>
<li><a href="https://www.youtube.com/watch?v=KFz0n7NLXJA">Polyglot web programming with Grails</a> by Jeff Brown</li>
<li><a href="https://www.youtube.com/watch?v=ObbQw6_VbWs">Angular.JS for the Grails enthusiast</a> by Will Buck</li>
<li><a href="https://www.youtube.com/watch?v=IGgsARk8BGE&amp;feature=youtu.be">Fine-grained security in Grails</a> presented by Andrew Miller</li>
</ul>
</li>
</ul>
<h2 id="google-posts">Google+ posts</h2>
<ul>
<li>The Groovy <a href="https://plus.google.com/u/0/100991326727521012956/posts/BzRTzUjRinF?cfem=1">markup template engine used as an alternative Magnolia CMS renderer</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li><a href="https://twitter.com/gvmtool/status/494515638669705216">GVM has passed 60,000 unique installs</a>!</li>
<li>Backward and binary compatibility is an important concern for the Groovy project, and Cédric Champeau just developed a <a href="https://twitter.com/cedricchampeau/status/494457760986456064">Gradle plugin integrated in the Groovy build to create reports of binary compatibility</a> between Groovy versions</li>
<li><a href="https://twitter.com/gvmtool/status/494482701567528960">Grails 2.4.3</a> available through GVM</li>
<li><a href="https://twitter.com/gvmtool/status/494827442641833984">Groovy 2.4.0-beta-2</a> available through GVM</li>
<li>The <a href="https://twitter.com/ratpackweb/status/494957092474605570">Ratpack project publishes its test coverage</a> and ask for help for improving it</li>
<li>David Norton asks the question: <a href="https://twitter.com/davidnortonjr/status/494197093524054017">how many actuaries in the world can put Groovy on their résumés</a>?</li>
<li><a href="https://twitter.com/ratpackweb/status/494985575863050240">Ratpack reminds us its got a forum</a> for user support</li>
<li>Senthil Gopal really <a href="https://twitter.com/sengopal/status/495593355032555521">enjoyed the Groovy puzzlers</a> by Baruch Sadogursky and Andrés Almiray at GR8Conf US 2014</li>
<li>The <a href="https://twitter.com/javafxpert/status/495921622730018816">JavaFX FXPlayground has added support for Groovy</a></li>
<li>Andrés Almiray remarks the <a href="https://twitter.com/aalmiray/status/496391849356300289">ongoing growth of Griffon plugins</a></li>
<li>Robert Guerra is <a href="https://twitter.com/robertoguerra19/status/496309476933517312">having fun with Ratpack and ElasticSearch</a></li>
<li>Discussing with Robert Fletcher reminded us of <a href="https://twitter.com/rfletcherew/status/496636379485728768">Groovy Koans</a>, for getting acquainted with Groovy</li>
<li>The <a href="https://twitter.com/lspacewalker/status/496640143097626624">Ratpack.io website is powered by the Groovy markup template engine</a></li>
<li>Andrés Almiray is using the <a href="https://twitter.com/aalmiray/status/496759565309186048">Gradle Git plugin</a> to easily push content to Github&rsquo;s pages</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Laurent Huet shows how to get the <a href="https://gist.github.com/lhuet/9cc05bab4417c0f1026f">list of twitter followers with Groovy and the Twitter4J</a> library</li>
<li>Jack Holt created a <a href="https://bitbucket.org/jackcholt/spring4hw">Spring MVC 4 demo using Groovy</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="https://2014.event.springone2gx.com/register">Early bird for SpringOne2GX 2014 ends August 9th</a>, be quick!</li>
<li><a href="http://www.meetup.com/Bangalore-Groovy-Grails-Meetup/">Bagalore Groovy meetup</a> next saturday</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy receives Geek Choice Award</title><link>https://glaforge.dev/posts/2014/08/01/groovy-receives-geek-choice-award/</link><pubDate>Fri, 01 Aug 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/08/01/groovy-receives-geek-choice-award/</guid><description>&lt;p>&lt;figure>
&lt;a href="#img-d479ec62a6d151ecb89ce8c5a9963c3f">
&lt;img src="https://glaforge.dev/img/misc/Geek-Choice-Awards-JVM-Language-300x300-black.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-d479ec62a6d151ecb89ce8c5a9963c3f">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/Geek-Choice-Awards-JVM-Language-300x300-black.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>&lt;a href="http://zeroturnaround.com/rebellabs/">RebelLabs&lt;/a> launched an annual study and awards to select 10 &amp;ldquo;kick-ass&amp;rdquo; technologies that developers love, and both Gradle and Groovy were among the winners!&lt;/p>
&lt;p>You can learn more about this &lt;a href="http://pages.zeroturnaround.com/Kickass-Technologies.html">&amp;ldquo;kick-ass&amp;rdquo; technology study&lt;/a> in the PDF report they crafted and which can be downloaded there (form filing required to download the PDF).&lt;/p>
&lt;p>Details are available for:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://zeroturnaround.com/rebellabs/10-kick-ass-technologies-modern-developers-love/4/">Groovy&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://zeroturnaround.com/rebellabs/10-kick-ass-technologies-modern-developers-love/3/">Gradle&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>It&amp;rsquo;s always nice to see such reports and awards recognizing the great work and energy the Groovy and Gradle teams have been put into developing those technologies, and making developers around the world delighted to work with them.&lt;/p></description><content:encoded>
<![CDATA[<p><figure>
  <a href="#img-d479ec62a6d151ecb89ce8c5a9963c3f">
    <img src="/img/misc/Geek-Choice-Awards-JVM-Language-300x300-black.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-d479ec62a6d151ecb89ce8c5a9963c3f">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/Geek-Choice-Awards-JVM-Language-300x300-black.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p><a href="http://zeroturnaround.com/rebellabs/">RebelLabs</a> launched an annual study and awards to select 10 &ldquo;kick-ass&rdquo; technologies that developers love, and both Gradle and Groovy were among the winners!</p>
<p>You can learn more about this <a href="http://pages.zeroturnaround.com/Kickass-Technologies.html">&ldquo;kick-ass&rdquo; technology study</a> in the PDF report they crafted and which can be downloaded there (form filing required to download the PDF).</p>
<p>Details are available for:</p>
<ul>
<li><a href="http://zeroturnaround.com/rebellabs/10-kick-ass-technologies-modern-developers-love/4/">Groovy</a></li>
<li><a href="http://zeroturnaround.com/rebellabs/10-kick-ass-technologies-modern-developers-love/3/">Gradle</a></li>
</ul>
<p>It&rsquo;s always nice to see such reports and awards recognizing the great work and energy the Groovy and Gradle teams have been put into developing those technologies, and making developers around the world delighted to work with them.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #32</title><link>https://glaforge.dev/posts/2014/07/30/groovy-weekly-32/</link><pubDate>Wed, 30 Jul 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/07/30/groovy-weekly-32/</guid><description>&lt;p>Live from GR8Conf US 2014, in Minneapolis, MN, USA! The code samples and slides of presentations are already flowing and are listed below in the presentations section. Lots of great content and talks to learn from.&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>Andrés Almiray &lt;a href="http://griffon-user.3225736.n2.nabble.com/ANN-Griffon-2-0-0-RC1-released-td7578844.html">released Griffon 2.0.0-rc-1&lt;/a> live on stage at GR8Conf US 2014, along with the announcement of the &lt;a href="http://new.griffon-framework.org/">new Griffon website&lt;/a>&lt;/li>
&lt;li>Cédric Champeau announces the &lt;a href="http://groovy.329449.n5.nabble.com/ANN-Groovy-2-3-5-released-and-upward-compatibility-td5720557.html#a5720571">releases of Groovy 2.3.6 and Groovy 2.4.0-beta-2&lt;/a>&lt;/li>
&lt;li>Guillaume Laforge announced &lt;a href="https://glaforge.dev/posts/2014/07/25/groovy-2-3-5-out-with-upward-compatibility/">Groovy 2.3.5 with upward compatibility&lt;/a>&lt;/li>
&lt;li>Cédric Champeau released &lt;a href="https://twitter.com/cedricchampeau/status/492594605003902976">v0.3.0 of the Gradle Groovy Android plugin&lt;/a>, allowing you to support Android libraries&lt;/li>
&lt;li>Cédric Champeau announces &lt;a href="https://twitter.com/cedricchampeau/status/493493646940184576">v0.1.2 of the JMH Gradle plugin&lt;/a>&lt;/li>
&lt;li>Kunal Dabir released &lt;a href="http://glide-gae.appspot.com">Glide 0.3.3&lt;/a>, with upgraded dependencies, and more goodness for Windows&lt;/li>
&lt;li>Cédric Champeau releases a &lt;a href="https://github.com/melix/japicmp-gradle-plugin">Japicmp Gradle plugin&lt;/a> to compare binary compatibility between JAR binaries&lt;/li>
&lt;li>&lt;a href="https://twitter.com/johnrengelman/status/494228881155424256">Gradle Shadow plugin 1.0.3&lt;/a> released&lt;/li>
&lt;/ul>
&lt;h2 id="news">News&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://www.elasticsearch.org/blog/elasticsearch-1-3-0-released/">ElasticSearch using Groovy scripting by default&lt;/a>&lt;/li>
&lt;li>The &lt;a href="http://new.griffon-framework.org/index.html">new Griffon website&lt;/a> is online!&lt;/li>
&lt;li>Dan Woods is contributing an &lt;a href="https://github.com/ratpack/ratpack/pull/395">Apache Camel module for Ratpack&lt;/a>&lt;/li>
&lt;li>Jacob Aae Mikkelsen published &lt;a href="http://grydeske.net/news/show/54">Grails Diary&lt;/a>, week 30&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>Ryan Vanderwerf on &lt;a href="http://rvanderwerf.blogspot.com/2014/07/lego-mindstorms-ev3-lejos-and-groovy.html">getting started with programming with Groovy on Lego Mindstorms&lt;/a> EV3&lt;/li>
&lt;li>Andrés Almiray&amp;rsquo;s &lt;a href="http://www.jroller.com/aalmiray/entry/gradle_glam_jacoco_coveralls">Gradle Glam: JaCoCo + CoverAlls&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://solidsoft.wordpress.com/2014/06/05/null-object-pattern-implementation-in-groovy-a-map-with-default-behavior/">NullObject pattern implementation&lt;/a> in Groovy, a map with default behavior, by Marcin Zajączkowski&lt;/li>
&lt;li>John Ferguson Smart wrote about data-driven testing with JUnit and highlighting &lt;a href="https://weblogs.java.net/blog/johnsmart/archive/2014/07/23/data-driven-unit-testing-java">Spock&amp;rsquo;s data-driven approach&lt;/a>&lt;/li>
&lt;li>Guide showing how to use &lt;a href="http://grooscript.org/grails-plugin/rest-demo.html">Grails&amp;rsquo; REST support from GSPs using GrooScript&lt;/a>&lt;/li>
&lt;li>Craig Burke writes about &lt;a href="http://www.craigburke.com/2014/07/24/angular-grails-asset-pipeline.html">Grails, Angular.JS and the Asset pipeline&lt;/a>&lt;/li>
&lt;li>Andrés Almiray shows how to &lt;a href="http://www.jroller.com/aalmiray/entry/how_to_test_javafx_services1">test JavaFX services, the Griffon way&lt;/a>&lt;/li>
&lt;li>Juan Vazquez wrote about adding map / filter / reduce &lt;a href="http://javazquez.com/juan/2013/02/05/add-map-reduce-and-filter-to-groovy-with-groovy-extension-modules/">method aliases with a Groovy extension module&lt;/a>&lt;/li>
&lt;li>André Steingreß explained how to create your own &lt;a href="http://blog.andresteingress.com/2013/01/25/groovy-2-1-type-checking-extensions/">Groovy type checking extensions&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://grooscript.org/grails-plugin/websocket-support.html">WebSocket support with GrooScript within Grails&lt;/a> applications&lt;/li>
&lt;li>&lt;a href="http://www.intelligrape.com/blog/2014/07/24/email-tracking-using-mailgun-with-grails-application/">Email tracking with MailGun in a Grails&lt;/a> application&lt;/li>
&lt;li>&lt;a href="http://www.intelligrape.com/blog/2014/07/24/jsoup-for-the-grails-soul/">Jsoup for the Grails soul&lt;/a>&lt;/li>
&lt;li>A &lt;a href="http://rominirani.com/2014/07/28/gradle-tutorial-series-an-overview/">Gradle tutorial series&lt;/a>&lt;/li>
&lt;li>Gradle Tutorial: Part 1 — &lt;a href="http://rominirani.com/2014/07/28/gradle-tutorial-part-1-installation-setup/">Installation + Setup&lt;/a>&lt;/li>
&lt;li>Gradle Tutorial: Part 2 — &lt;a href="http://rominirani.com/2014/07/28/gradle-tutorial-part-2-java-projects/">Java Projects&lt;/a>&lt;/li>
&lt;li>Gradle Tutorial: Part 3 — &lt;a href="http://rominirani.com/2014/07/29/gradle-tutorial-part-3-multiple-java-projects/">Multiple Java Projects&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.infoq.com/news/2014/07/elasticsearch-1-3-0">InfoQ on ElasticSearch using Groovy for scripting&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau demonstrates the &lt;a href="http://www.youtube.com/watch?v=AebkFsfcuDg&amp;amp;feature=youtu.be&amp;amp;a">Groovy support for developing Android applications&lt;/a>&lt;/li>
&lt;li>Jeff Beck published his GR8Conf US 2014 &lt;a href="https://github.com/beckje01/ratpack-subscription-api">Ratpack intro presentation and code&lt;/a>&lt;/li>
&lt;li>Colin Harrington published his GR8Conf US 2014 presentation slides on &lt;a href="http://slides.com/colinharrington/how-to-test-your-grails-application#/">how to test your Grails application&lt;/a>&lt;/li>
&lt;li>Colin Harrington on &lt;a href="http://slides.com/colinharrington/functional-testing-your-grails-app-with-geb--2#/">functional testing your Grails apps with Geb&lt;/a>, at GR8Conf US 2014&lt;/li>
&lt;li>Ryan Vanderwerf on &lt;a href="https://twitter.com/ryanvanderwerf/status/493895466480717825">getting Grails / Groovy into the enterprise&lt;/a>, at GR8Conf US 2014&lt;/li>
&lt;li>Ted Naleid on us&lt;a href="https://speakerdeck.com/tednaleid/using-grails-asset-pipeline-plugin">ing the Grails Asset pipeline&lt;/a>, presented at GR8Conf US 2014&lt;/li>
&lt;li>&lt;a href="http://www.slideshare.net/lhotari/performance-tuning-grails-applications-applications-gr8-confus">Performance tuning Grails applications&lt;/a> by Lari Hotari at GR8Conf US 2014&lt;/li>
&lt;li>&lt;a href="https://github.com/onetribeyoyo/fine-grained-security">Fine-grained security&lt;/a> by Andy Miller at GR8Conf US 2014&lt;/li>
&lt;li>&lt;a href="http://fr.slideshare.net/lhotari/ratpack-and-grails-3-gr8confus">Grails 3 and Ratpack, Micro-services&lt;/a>, and more, by Lari Hotari, at GR8Conf US 2014&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gr8confus/status/494255220654764032">GR8Conf US 2014 slides and samples&lt;/a> are being collected on Github&lt;/li>
&lt;/ul>
&lt;h2 id="mailing-list-posts">Mailing-list posts&lt;/h2>
&lt;ul>
&lt;li>Interesting thread on the Groovy user list about &lt;a href="http://groovy.329449.n5.nabble.com/Advocating-Groovy-vs-Java-td5720573.html">Groovy performance, advocating Groovy vs Java&lt;/a>, and more&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>How &lt;a href="https://twitter.com/targetcareers/status/494156672420831232">Target is embracing Groovy&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/highlightjs/status/492421799410282496">Highlight.js now supports Groovy syntax highlighting&lt;/a>&lt;/li>
&lt;li>Guillaume Laforge contributed a &lt;a href="https://twitter.com/glaforge/status/492206420017049600">Groovy logo for the devicons font&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/492244790101426176">Vert.x 2.1.2&lt;/a> is available through GVM&lt;/li>
&lt;li>Angel Ruiz likes Java 8 streams but &lt;a href="https://twitter.com/aruizca/status/492551332218159104">loves Tim Yates&amp;rsquo; Groovy streams&lt;/a> even more&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/493004971810762752">Glide 0.3.3&lt;/a> available through GVM&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/493042458318864384">Groovy 2.3.5&lt;/a> available through GVM&lt;/li>
&lt;li>Jeff Beck suggests to &lt;a href="https://twitter.com/sheetsj/status/493841650217545728">use GVM and Lazybones for getting started with Ratpack&lt;/a>&lt;/li>
&lt;li>A new Groovy-related account to follow on Twitter with &lt;a href="https://twitter.com/groovypuzzlers/status/493821368157220865">Groovy puzzlers&lt;/a>&lt;/li>
&lt;li>Ken Kousen showed the nice &lt;a href="https://twitter.com/rfletcherew/status/494138116043997184">Grails url-mapping-reports command&lt;/a> on stage&lt;/li>
&lt;li>Rob Fletcher claims &lt;a href="https://twitter.com/elanorriley/status/494205248500555778">Ratpack is your gateway drug to non-blocking&lt;/a>&lt;/li>
&lt;li>Thanks to the GR8Ladies work, we&amp;rsquo;re seeing &lt;a href="https://twitter.com/wmacgyver/status/494234801348812801">more women participating to GR8Conf&lt;/a>&lt;/li>
&lt;li>Scott Hickey is still &lt;a href="https://twitter.com/jshickey/status/494263654166982657">impressed, amazed and appreciative of the Groovy community&lt;/a> after 9 years of using Groovy in various enterprise projects&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>Andrés Almiray shows how to &lt;a href="https://gist.github.com/aalmiray/e6f54aa4b3803be0bcac">aggregate JaCoCo reports in a multi-project Gradle build&lt;/a>&lt;/li>
&lt;li>Luke Daley adds &lt;a href="https://github.com/ratpack/ratpack/blob/master/ratpack-test/src/test/groovy/ratpack/test/exec/ExecHarnessSpec.groovy#L27">test support for async support in Ratpack&lt;/a>&lt;/li>
&lt;li>Andrey Hihlovskiy shows a snippet &lt;a href="https://twitter.com/andreyhihlovski/status/492930646252457984">removing parts of a string with regular expressions&lt;/a>&lt;/li>
&lt;li>Cédric Champeau shows an example &lt;a href="https://twitter.com/cedricchampeau/status/492611846953005056">usage of the Groovy Android Gradle plugin with an Android library&lt;/a>&lt;/li>
&lt;li>MrHaki’s GR8Conf US 2014 G* Goodness talks’ samples:
&lt;ul>
&lt;li>&lt;a href="https://github.com/mrhaki/gr8conf2014us-gradle-goodness">code samples of his Gradle Goodness presentation&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/mrhaki/gr8conf2014us-grails-goodness">code samples of his Grails Goodness presentation&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/mrhaki/gr8conf2014us-groovy-goodness">code samples of his Groovy Goodness presentation&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Erik Pragt shares a snippet of code showing &lt;a href="https://gist.github.com/bodiam/2ab1b98a1abb33f5ba1b">exceptions implementing a no-stacktrace trait to make exceptions fast and cheap&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="books">Books&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/ManningBooks/status/492749460603432960">Grails in Action, 2nd edition, is available in epub and kindle formats&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://2014.event.springone2gx.com/register">Early bird for SpringOne2GX is ending on August 9th&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="jobs">Jobs&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/danveloper/status/494214232343203840">Target is hiring Groovy developers&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>Live from GR8Conf US 2014, in Minneapolis, MN, USA! The code samples and slides of presentations are already flowing and are listed below in the presentations section. Lots of great content and talks to learn from.</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Andrés Almiray <a href="http://griffon-user.3225736.n2.nabble.com/ANN-Griffon-2-0-0-RC1-released-td7578844.html">released Griffon 2.0.0-rc-1</a> live on stage at GR8Conf US 2014, along with the announcement of the <a href="http://new.griffon-framework.org/">new Griffon website</a></li>
<li>Cédric Champeau announces the <a href="http://groovy.329449.n5.nabble.com/ANN-Groovy-2-3-5-released-and-upward-compatibility-td5720557.html#a5720571">releases of Groovy 2.3.6 and Groovy 2.4.0-beta-2</a></li>
<li>Guillaume Laforge announced <a href="https://glaforge.dev/posts/2014/07/25/groovy-2-3-5-out-with-upward-compatibility/">Groovy 2.3.5 with upward compatibility</a></li>
<li>Cédric Champeau released <a href="https://twitter.com/cedricchampeau/status/492594605003902976">v0.3.0 of the Gradle Groovy Android plugin</a>, allowing you to support Android libraries</li>
<li>Cédric Champeau announces <a href="https://twitter.com/cedricchampeau/status/493493646940184576">v0.1.2 of the JMH Gradle plugin</a></li>
<li>Kunal Dabir released <a href="http://glide-gae.appspot.com">Glide 0.3.3</a>, with upgraded dependencies, and more goodness for Windows</li>
<li>Cédric Champeau releases a <a href="https://github.com/melix/japicmp-gradle-plugin">Japicmp Gradle plugin</a> to compare binary compatibility between JAR binaries</li>
<li><a href="https://twitter.com/johnrengelman/status/494228881155424256">Gradle Shadow plugin 1.0.3</a> released</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li><a href="http://www.elasticsearch.org/blog/elasticsearch-1-3-0-released/">ElasticSearch using Groovy scripting by default</a></li>
<li>The <a href="http://new.griffon-framework.org/index.html">new Griffon website</a> is online!</li>
<li>Dan Woods is contributing an <a href="https://github.com/ratpack/ratpack/pull/395">Apache Camel module for Ratpack</a></li>
<li>Jacob Aae Mikkelsen published <a href="http://grydeske.net/news/show/54">Grails Diary</a>, week 30</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Ryan Vanderwerf on <a href="http://rvanderwerf.blogspot.com/2014/07/lego-mindstorms-ev3-lejos-and-groovy.html">getting started with programming with Groovy on Lego Mindstorms</a> EV3</li>
<li>Andrés Almiray&rsquo;s <a href="http://www.jroller.com/aalmiray/entry/gradle_glam_jacoco_coveralls">Gradle Glam: JaCoCo + CoverAlls</a></li>
<li><a href="http://solidsoft.wordpress.com/2014/06/05/null-object-pattern-implementation-in-groovy-a-map-with-default-behavior/">NullObject pattern implementation</a> in Groovy, a map with default behavior, by Marcin Zajączkowski</li>
<li>John Ferguson Smart wrote about data-driven testing with JUnit and highlighting <a href="https://weblogs.java.net/blog/johnsmart/archive/2014/07/23/data-driven-unit-testing-java">Spock&rsquo;s data-driven approach</a></li>
<li>Guide showing how to use <a href="http://grooscript.org/grails-plugin/rest-demo.html">Grails&rsquo; REST support from GSPs using GrooScript</a></li>
<li>Craig Burke writes about <a href="http://www.craigburke.com/2014/07/24/angular-grails-asset-pipeline.html">Grails, Angular.JS and the Asset pipeline</a></li>
<li>Andrés Almiray shows how to <a href="http://www.jroller.com/aalmiray/entry/how_to_test_javafx_services1">test JavaFX services, the Griffon way</a></li>
<li>Juan Vazquez wrote about adding map / filter / reduce <a href="http://javazquez.com/juan/2013/02/05/add-map-reduce-and-filter-to-groovy-with-groovy-extension-modules/">method aliases with a Groovy extension module</a></li>
<li>André Steingreß explained how to create your own <a href="http://blog.andresteingress.com/2013/01/25/groovy-2-1-type-checking-extensions/">Groovy type checking extensions</a></li>
<li><a href="http://grooscript.org/grails-plugin/websocket-support.html">WebSocket support with GrooScript within Grails</a> applications</li>
<li><a href="http://www.intelligrape.com/blog/2014/07/24/email-tracking-using-mailgun-with-grails-application/">Email tracking with MailGun in a Grails</a> application</li>
<li><a href="http://www.intelligrape.com/blog/2014/07/24/jsoup-for-the-grails-soul/">Jsoup for the Grails soul</a></li>
<li>A <a href="http://rominirani.com/2014/07/28/gradle-tutorial-series-an-overview/">Gradle tutorial series</a></li>
<li>Gradle Tutorial: Part 1 — <a href="http://rominirani.com/2014/07/28/gradle-tutorial-part-1-installation-setup/">Installation + Setup</a></li>
<li>Gradle Tutorial: Part 2 — <a href="http://rominirani.com/2014/07/28/gradle-tutorial-part-2-java-projects/">Java Projects</a></li>
<li>Gradle Tutorial: Part 3 — <a href="http://rominirani.com/2014/07/29/gradle-tutorial-part-3-multiple-java-projects/">Multiple Java Projects</a></li>
<li><a href="http://www.infoq.com/news/2014/07/elasticsearch-1-3-0">InfoQ on ElasticSearch using Groovy for scripting</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Cédric Champeau demonstrates the <a href="http://www.youtube.com/watch?v=AebkFsfcuDg&amp;feature=youtu.be&amp;a">Groovy support for developing Android applications</a></li>
<li>Jeff Beck published his GR8Conf US 2014 <a href="https://github.com/beckje01/ratpack-subscription-api">Ratpack intro presentation and code</a></li>
<li>Colin Harrington published his GR8Conf US 2014 presentation slides on <a href="http://slides.com/colinharrington/how-to-test-your-grails-application#/">how to test your Grails application</a></li>
<li>Colin Harrington on <a href="http://slides.com/colinharrington/functional-testing-your-grails-app-with-geb--2#/">functional testing your Grails apps with Geb</a>, at GR8Conf US 2014</li>
<li>Ryan Vanderwerf on <a href="https://twitter.com/ryanvanderwerf/status/493895466480717825">getting Grails / Groovy into the enterprise</a>, at GR8Conf US 2014</li>
<li>Ted Naleid on us<a href="https://speakerdeck.com/tednaleid/using-grails-asset-pipeline-plugin">ing the Grails Asset pipeline</a>, presented at GR8Conf US 2014</li>
<li><a href="http://www.slideshare.net/lhotari/performance-tuning-grails-applications-applications-gr8-confus">Performance tuning Grails applications</a> by Lari Hotari at GR8Conf US 2014</li>
<li><a href="https://github.com/onetribeyoyo/fine-grained-security">Fine-grained security</a> by Andy Miller at GR8Conf US 2014</li>
<li><a href="http://fr.slideshare.net/lhotari/ratpack-and-grails-3-gr8confus">Grails 3 and Ratpack, Micro-services</a>, and more, by Lari Hotari, at GR8Conf US 2014</li>
<li><a href="https://twitter.com/gr8confus/status/494255220654764032">GR8Conf US 2014 slides and samples</a> are being collected on Github</li>
</ul>
<h2 id="mailing-list-posts">Mailing-list posts</h2>
<ul>
<li>Interesting thread on the Groovy user list about <a href="http://groovy.329449.n5.nabble.com/Advocating-Groovy-vs-Java-td5720573.html">Groovy performance, advocating Groovy vs Java</a>, and more</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>How <a href="https://twitter.com/targetcareers/status/494156672420831232">Target is embracing Groovy</a></li>
<li><a href="https://twitter.com/highlightjs/status/492421799410282496">Highlight.js now supports Groovy syntax highlighting</a></li>
<li>Guillaume Laforge contributed a <a href="https://twitter.com/glaforge/status/492206420017049600">Groovy logo for the devicons font</a></li>
<li><a href="https://twitter.com/gvmtool/status/492244790101426176">Vert.x 2.1.2</a> is available through GVM</li>
<li>Angel Ruiz likes Java 8 streams but <a href="https://twitter.com/aruizca/status/492551332218159104">loves Tim Yates&rsquo; Groovy streams</a> even more</li>
<li><a href="https://twitter.com/gvmtool/status/493004971810762752">Glide 0.3.3</a> available through GVM</li>
<li><a href="https://twitter.com/gvmtool/status/493042458318864384">Groovy 2.3.5</a> available through GVM</li>
<li>Jeff Beck suggests to <a href="https://twitter.com/sheetsj/status/493841650217545728">use GVM and Lazybones for getting started with Ratpack</a></li>
<li>A new Groovy-related account to follow on Twitter with <a href="https://twitter.com/groovypuzzlers/status/493821368157220865">Groovy puzzlers</a></li>
<li>Ken Kousen showed the nice <a href="https://twitter.com/rfletcherew/status/494138116043997184">Grails url-mapping-reports command</a> on stage</li>
<li>Rob Fletcher claims <a href="https://twitter.com/elanorriley/status/494205248500555778">Ratpack is your gateway drug to non-blocking</a></li>
<li>Thanks to the GR8Ladies work, we&rsquo;re seeing <a href="https://twitter.com/wmacgyver/status/494234801348812801">more women participating to GR8Conf</a></li>
<li>Scott Hickey is still <a href="https://twitter.com/jshickey/status/494263654166982657">impressed, amazed and appreciative of the Groovy community</a> after 9 years of using Groovy in various enterprise projects</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Andrés Almiray shows how to <a href="https://gist.github.com/aalmiray/e6f54aa4b3803be0bcac">aggregate JaCoCo reports in a multi-project Gradle build</a></li>
<li>Luke Daley adds <a href="https://github.com/ratpack/ratpack/blob/master/ratpack-test/src/test/groovy/ratpack/test/exec/ExecHarnessSpec.groovy#L27">test support for async support in Ratpack</a></li>
<li>Andrey Hihlovskiy shows a snippet <a href="https://twitter.com/andreyhihlovski/status/492930646252457984">removing parts of a string with regular expressions</a></li>
<li>Cédric Champeau shows an example <a href="https://twitter.com/cedricchampeau/status/492611846953005056">usage of the Groovy Android Gradle plugin with an Android library</a></li>
<li>MrHaki’s GR8Conf US 2014 G* Goodness talks’ samples:
<ul>
<li><a href="https://github.com/mrhaki/gr8conf2014us-gradle-goodness">code samples of his Gradle Goodness presentation</a></li>
<li><a href="https://github.com/mrhaki/gr8conf2014us-grails-goodness">code samples of his Grails Goodness presentation</a></li>
<li><a href="https://github.com/mrhaki/gr8conf2014us-groovy-goodness">code samples of his Groovy Goodness presentation</a></li>
</ul>
</li>
<li>Erik Pragt shares a snippet of code showing <a href="https://gist.github.com/bodiam/2ab1b98a1abb33f5ba1b">exceptions implementing a no-stacktrace trait to make exceptions fast and cheap</a></li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li><a href="https://twitter.com/ManningBooks/status/492749460603432960">Grails in Action, 2nd edition, is available in epub and kindle formats</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="https://2014.event.springone2gx.com/register">Early bird for SpringOne2GX is ending on August 9th</a></li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li><a href="https://twitter.com/danveloper/status/494214232343203840">Target is hiring Groovy developers</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.3.5 out with upward compatibility</title><link>https://glaforge.dev/posts/2014/07/25/groovy-2-3-5-out-with-upward-compatibility/</link><pubDate>Fri, 25 Jul 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/07/25/groovy-2-3-5-out-with-upward-compatibility/</guid><description>&lt;p>The Groovy team is pleased to announce the release of Groovy 2.3.5.&lt;/p>
&lt;p>Groovy 2.3.5 is a bug fix release of our Groovy 2.3 branch.&lt;/p>
&lt;p>You’ll find fixes for static compilation and type-checking, JSON serialization issues, markup template engine errors, and performance improvements.&lt;/p>
&lt;p>We care a lot about backward and binary compatibility, but in this release, we also thought about upward compatibility, so that code compiled with a newer version can even run on an older runtime.&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy team is pleased to announce the release of Groovy 2.3.5.</p>
<p>Groovy 2.3.5 is a bug fix release of our Groovy 2.3 branch.</p>
<p>You’ll find fixes for static compilation and type-checking, JSON serialization issues, markup template engine errors, and performance improvements.</p>
<p>We care a lot about backward and binary compatibility, but in this release, we also thought about upward compatibility, so that code compiled with a newer version can even run on an older runtime.</p>
<p>So we leveraged this version to add a new artifact, named groovy-backports-compat23. This artifact shouldn’t be necessary for most of you, but if you face an error like:</p>
<pre tabindex="0"><code>Caused by: java.lang.ClassNotFoundException: org.codehaus.groovy.runtime.typehandling.ShortTypeHandling
  at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
</code></pre><p>in your project, then it means that a class has been compiled with Groovy 2.3+ but that you are trying to use it with an older version of Groovy. By adding this jar on classpath, you give a chance to your program to run. This may be particularily interesting for Gradle users that want to use a plugin built on Gradle 2+ on older versions of Gradle and face this error. Adding the following line to their build files should help:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>buildscript <span style="color:#666">{</span>  
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// ...  
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    dependencies <span style="color:#666">{</span>  
</span></span><span style="display:flex;"><span>        classpath <span style="color:#4070a0">&#39;some plugin build on gradle 2&#39;</span>  
</span></span><span style="display:flex;"><span>        classpath <span style="color:#4070a0">&#39;org.codehaus.groovy:groovy-backports-compat23:2.3.5&#39;</span>  
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span> 
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Note that for now, this jar only contains the ShortTypeHandlingClass. Future versions may include more.</p>
<p>You can download Groovy 2.3.5 here:
<a href="http://beta.groovy-lang.org/download.html">http://beta.groovy-lang.org/download.html</a></p>
<p>The detailed JIRA release notes can be found here:
<a href="https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=20491">https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=20491</a></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #31</title><link>https://glaforge.dev/posts/2014/07/22/groovy-weekly-31/</link><pubDate>Tue, 22 Jul 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/07/22/groovy-weekly-31/</guid><description>&lt;p>The news keep flowing even in the summer!&lt;/p>
&lt;p>It’s probably not new to you, my readers, but Groovy and Grails are kick-ass technologies, as reported by the RebelLabs report.&lt;/p>
&lt;p>And did you know that Groovy is now the default scripting technology in the popular ElasticSearch? Or used by Jenkins in its Job DSL?&lt;/p>
&lt;p>We even have rumors, with the potential acquisition of Groovy-powered SmartThings IoT platform by Samsung!&lt;/p>
&lt;p>We also have more videos available of presentations from GR8Conf Europe, to prepare yourself for those who are attending GR8Conf US next week! See you there!&lt;/p></description><content:encoded>
<![CDATA[<p>The news keep flowing even in the summer!</p>
<p>It’s probably not new to you, my readers, but Groovy and Grails are kick-ass technologies, as reported by the RebelLabs report.</p>
<p>And did you know that Groovy is now the default scripting technology in the popular ElasticSearch? Or used by Jenkins in its Job DSL?</p>
<p>We even have rumors, with the potential acquisition of Groovy-powered SmartThings IoT platform by Samsung!</p>
<p>We also have more videos available of presentations from GR8Conf Europe, to prepare yourself for those who are attending GR8Conf US next week! See you there!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://github.com/groovy/groovy-eclipse/wiki/Groovy-Eclipse-2.9.0-Release-Notes">Groovy Eclipse 2.9.0</a> released</li>
<li><a href="https://spring.io/blog/2014/07/21/spring-framework-4-1-release-candidate-available">Spring Framework 4.1 RC</a> is out with Spring Web MVC support for Groovy markup templates</li>
<li>Andrés Almiray created a <a href="https://github.com/aalmiray/stats-gradle-plugin">Gradle Stats plugin</a> to gather some metrics on your project</li>
<li>Andy Wilkinson released a plugin to <a href="https://github.com/spring-gradle-plugins/dependency-management-plugin">add Maven-like dependency management to Gradle</a></li>
<li>New version of the <a href="https://twitter.com/grooscript/status/491538345265491969">GrooScript Grails 2.4 plugin</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Rebel Labs selects <a href="http://zeroturnaround.com/rebellabs/10-kick-ass-technologies-modern-developers-love/">Groovy and Gradle in their 10 kick-ass technologies</a> developers love</li>
<li><a href="https://twitter.com/robertoguerra19/status/491246418137714688">ElasticSearch&rsquo;s default scripting engine is now Groovy</a></li>
<li>Guillaume Laforge worked on making the <a href="http://beta.groovy-lang.org/">future Groovy website fully responsive</a></li>
<li>Rumor has it that Internet of Things startup <a href="http://gigaom.com/2014/07/15/if-samsung-buys-smartthings-its-a-win-for-both/">SmartThings using Groovy for orchestrating &ldquo;things&rdquo; in talks to be acquired by Samsung</a> for $200 million</li>
<li>A Gradle plugin to <a href="https://code.google.com/p/gradle-macappbundle/">bundle MacOS X applications of your Groovy / Java project</a></li>
<li><a href="https://github.com/grails/grails-core">Grails just crossed the 1000 stars on Github</a>, still time to star it!</li>
<li>Jacob Aae Mikkelsen is back with the <a href="http://grydeske.net/news/show/53">Grails Diary</a>!</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Cédric Champeau describes the <a href="http://melix.github.io/blog/2014/07/new-groovy-website.html">technical details of the new Groovy website</a></li>
<li>DrDobbs features an article by Benjamin Muschko on <a href="http://www.drdobbs.com/open-source/writing-build-scripts-with-gradle/240168648">writing build scripts with Gradle</a></li>
<li>Andrés Almiray inaugurates a series of blog posts on cool Gradle plugins, starting with the <a href="http://www.jroller.com/aalmiray/entry/gradle_glam_versions">versions Gradle plugin</a></li>
<li>André Steingreß on <a href="http://blog.andresteingress.com/2014/07/22/spock-junit-rules/">JUnit rules with Spock</a></li>
<li>The <a href="http://www.oraclejavamagazine-digital.com/javamagazine/july_august_2014/?sub_id=BUXrE7pqvqBhE&amp;folio=67#pg68">Java magazine mentions Groovy</a> in two articles, one on JavaFX showing how neat GroovyFX is, and the other on cool aspects of various alternative languages</li>
<li>Peter Ledbrook explains how to <a href="http://blog.cacoethes.co.uk/groovyandgrails/adding-spring-security-users-in-bulk-in-grails">add Spring Security users in bulk in your Grails</a> application</li>
<li>Rob Fletcher looks into <a href="http://blog.freeside.co/2014/07/21/closures-and-field-visibility/">Groovy closures and field visibility</a></li>
<li>The Gretty project can now <a href="http://akhikhl.github.io/gretty-doc/Product-generation.html">generate runnable products</a></li>
<li>Using <a href="http://www.intelligrape.com/blog/2014/07/17/using-spring-events-in-grails/">Spring events in Grails</a> by Madhave Khanna</li>
<li>Craig Atkinson blogs about <a href="http://www.objectpartners.com/2014/07/15/grails-api-functional-testing/">Grails API functional testing</a></li>
<li>Marco Pas details an issue when <a href="http://mpas.github.io/post/2014/07/upgrade-grails-2.4.2/">migrating from Grails 2.3.8 to 2.4.2</a></li>
<li>Igor Shults illustrates <a href="http://www.objectpartners.com/2014/07/09/groovys-with-and-multiple-assignment/">Groovy’s with() and multiple assignments</a></li>
<li>Ravi Kumar on the <a href="http://www.oodlestechnologies.com/blogs/Importance-of-Grails-asset-pipeline-plugin">importance of the Grails asset pipeline</a></li>
<li>Archna Dhingra writes about <a href="http://www.oodlestechnologies.com/blogs/Ways-to-access-grailsApplication-in-Grails-Project">ways to access grailsApplication in Grails</a></li>
<li><a href="http://www.oodlestechnologies.com/blogs/Create-Default-Data-and-Test-Data-the-Easy-Way-Using-Fixture-and-Build-Test-Data-plugins">Create default data and test data the easy way using fixture and build test data plugins</a> by Ankit Nigam</li>
<li>Shiv kumar explains how to <a href="http://www.oodlestechnologies.com/blogs/How-to-use-handlebars-in-grails">use handlebars in Grails</a></li>
<li>Using the <a href="http://www.oodlestechnologies.com/blogs/Drools-Planner-Example-using-Grails">Drools Planner from Grails</a> by Alok Kumar Swain</li>
</ul>
<h2 id="presentations--gr8conf-europe-2014">Presentations — GR8Conf Europe 2014</h2>
<ul>
<li><a href="https://www.youtube.com/watch?v=LZQ-1f9RGqg&amp;feature=youtu.be&amp;a">Cut your Grails application to pieces, build feature plugins</a>, by Göran Ehrsson</li>
<li><a href="https://www.youtube.com/watch?v=4LLQeMcFUpY&amp;feature=youtu.be&amp;a">Micro-service architecture with Spring Boot, Groovy</a> and friends, by Marco Vermeulen</li>
<li><a href="https://www.youtube.com/watch?v=McFoNTKD8Q0&amp;feature=youtu.be&amp;a">Functional testing your Grails apps with Geb</a>, by Collin Harrington</li>
<li><a href="https://www.youtube.com/watch?v=d_w7v-Cy8q4&amp;feature=youtu.be&amp;a">Performance tuning Grails applications</a>, by Lari Hotari, at GR8Conf Europe 2014</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Cédric Champeau claims that <a href="https://twitter.com/cedricchampeau/status/489316376696598528">if Netflix can use Groovy, you can too</a>!</li>
<li>Kohsuke Kawaguchi invites us to play in the playground of the <a href="https://twitter.com/kohsukekawa/status/489333038573039617">Groovy-based Jenkins job DSL plugin</a></li>
<li>Cédric Champeau reminds us about the creation of a <a href="https://twitter.com/cedricchampeau/status/489310387272421376">HipChat public channel for Groovy</a> if you want to chat with Groovy users and developers</li>
<li>Marco Vermeulen realizes that <a href="https://twitter.com/marcovermeulen/status/491232279008911361">Spring Boot supports Spock for testing</a> out of the box</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Michael van Niekerk created a <a href="https://github.com/mvniekerk/GradleGroovyRobot">proof of concept running a Groovy application on iOS</a></li>
<li>The <a href="https://twitter.com/grooscript/status/489190810282643456">2048 web game developed with GrooScript</a></li>
<li>Cédric Champeau shares a snippet to <a href="https://gist.github.com/melix/628b2f04f83f7f9721b8">dump the AST transformations being used</a></li>
<li>Guillaume Laforge is contributing <a href="https://github.com/isagalaev/highlight.js/pull/512/files">Groovy syntax highlighting support to the Highlight.js</a> JavaScript syntax highlighting library</li>
<li>Danny Hyun created a <a href="https://github.com/danhyun/ratpack-pixel-service">&ldquo;pixel service&rdquo; with Ratpack</a> for user tracking</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="https://twitter.com/gr8confus/status/491629430700535809">JFrog is a sponsor of GR8Conf US</a> next week</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #30</title><link>https://glaforge.dev/posts/2014/07/15/groovy-weekly-30/</link><pubDate>Tue, 15 Jul 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/07/15/groovy-weekly-30/</guid><description>&lt;p>The &lt;a href="http://beta.groovy-lang.org/">new Groovy website&lt;/a> announced last week keeps on improving, thanks to your feedback and already many contributions through pull requests on Github!&lt;/p>
&lt;p>To wet your appetite for GR8Conf US 2014 at the end of the month, we also have more GR8Conf Europe 2014 videos published last week!&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/martinlippert/status/487631673836965888">Groovy Grails ToolSuite 3.6.0 out with Groovy 2.3 and Grails 2.4&lt;/a> support on Eclipse 4.4&lt;/li>
&lt;li>&lt;a href="https://twitter.com/grooscript/status/488453854032560128">GrooScript 0.5.2&lt;/a> is out&lt;/li>
&lt;li>The &lt;a href="http://beta.groovy-lang.org/download.html">Windows installer for Groovy 2.3.4&lt;/a> is available thanks to Joachim Baumann&lt;/li>
&lt;li>Andrés Almiray released the &lt;a href="https://twitter.com/aalmiray/status/487255344234905600">Asciidoctor Gradle plugin v0.7.3&lt;/a> which is compatible with JDK 6+&lt;/li>
&lt;/ul>
&lt;h2 id="news">News&lt;/h2>
&lt;ul>
&lt;li>Kunal Dabir created an “&lt;a href="https://github.com/kdabir/awesome-groovy">awesome-groovy&lt;/a>” Github project providing a curated list of interesting Groovy-based projects&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>Benjamin Muschko explains &lt;a href="http://www.drdobbs.com/jvm/why-build-your-java-projects-with-gradle/240168608">why build your Java projects with Gradle&lt;/a> rather than Ant or Maven&lt;/li>
&lt;li>Guillaume Laforge gathered the &lt;a href="https://glaforge.dev/posts/2014/07/13/feedback-and-actions-for-the-new-groovy-website/">feedback the team received about the new Groovy website&lt;/a>&lt;/li>
&lt;li>Roberto Guerra writes about &lt;a href="http://blog.stumblingoncode.com/posts/2014-07-11-ratpack-templates.html">Ratpack&amp;rsquo;s templates&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.sven-ehrke.de/asciidocblog/switch_dependencies_binary_source.html">Use Gradle to switch a dependency from binary to source&lt;/a> mode and back, by Sven Ehrke&lt;/li>
&lt;li>Grails Goodness by MrHaki
&lt;ul>
&lt;li>&lt;a href="http://mrhaki.blogspot.fr/2014/07/grails-goodness-change-response-formats.html">change response formats in RestfulController&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://mrhaki.blogspot.fr/2014/07/grails-goodness-custom-controller-class.html">custom controller class with resource Annotation&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://mrhaki.blogspot.fr/2014/07/grails-goodness-using-converter-named.html">using converter named configurations with default renderers&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Pawel Oczadly reveals a Gradle tip: &lt;a href="http://paweloczadly.github.io/dev/2014/07/09/gradle-set-different-name-for-wrapper-and-default-build-script/">set a different name for wrapper and the default build script&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="presentations--gr8conf-europe-2014">Presentations — GR8Conf Europe 2014&lt;/h2>
&lt;ul>
&lt;li>Noam Tenne delivered a &lt;a href="https://www.youtube.com/watch?v=GfIhxi7L6R0&amp;amp;feature=youtu.be&amp;amp;a">fantastic Groovy puzzler talk&lt;/a>&lt;/li>
&lt;li>Graeme Rocher talks about &lt;a href="https://www.youtube.com/watch?v=NZH9n3s8Ai0&amp;amp;feature=youtu.be&amp;amp;a">Grails 3 and beyond&lt;/a>&lt;/li>
&lt;li>Andrés Almiray on &lt;a href="https://www.youtube.com/watch?v=hrM_AtD5eCw&amp;amp;feature=youtu.be&amp;amp;a">what&amp;rsquo;s new and coming in Griffon&lt;/a>&lt;/li>
&lt;li>Lari Hotari on &lt;a href="https://www.youtube.com/watch?v=_JVeuC8R5BM&amp;amp;feature=youtu.be&amp;amp;a">Grails 3 and Ratpack&lt;/a>&lt;/li>
&lt;li>Peter Ledbrook guides you through &lt;a href="https://www.youtube.com/watch?v=pzZARB7SxdA&amp;amp;feature=youtu.be&amp;amp;a">bootstrapping your projects with Lazybones&lt;/a>&lt;/li>
&lt;li>Jeff Brown shows you how you can do &lt;a href="https://www.youtube.com/watch?v=L-sH9Bn9y_c&amp;amp;feature=youtu.be&amp;amp;a">polyglot web development with Grails 2&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="mailing-list-posts">Mailing-list posts&lt;/h2>
&lt;ul>
&lt;li>Dylan Bijnagte announces Spock-Genesis: &lt;a href="http://groovy.329449.n5.nabble.com/ANN-spock-genesis-initial-release-0-1-0-td5720402.html">data generators for data-driven / property based testing with Spock&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="google-posts">Google+ posts&lt;/h2>
&lt;ul>
&lt;li>LinYuan Zheng launched an effort to &lt;a href="https://plus.google.com/b/113675159854671799959/106541219437002995923/posts/hcKBXa3tSkW?cfem=1">translate the Groovy documentation in Chinese&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>Marco Vermeulen is looking for a &lt;a href="https://twitter.com/marcoVermeulen/status/488436820456525824">PaaS sponsor to host GVM&lt;/a>&lt;/li>
&lt;li>Dan Woods measured &lt;a href="https://twitter.com/danveloper/status/488138482280189952">Ratpack&amp;rsquo;s great stable performance at 52K requests/second&lt;/a> on a large Amazon EC2 instance&lt;/li>
&lt;li>Cédric Champeau notices that in a matter of hours, the new &lt;a href="https://twitter.com/cedricchampeau/status/486627877656752128">Groovy website project on Github received 3 pull requests&lt;/a>&lt;/li>
&lt;li>As noted by Vladimír Oraný, the &lt;a href="https://twitter.com/musketyr/status/486739565265555456">&amp;ldquo;improve the doc&amp;rdquo; button on the new Groovy web site makes it easy to contribute&lt;/a> improvements to the site&lt;/li>
&lt;li>Andrés Almiray choses &lt;a href="https://twitter.com/aalmiray/status/486951644874432512">JBake and Asciidoctor for the upcoming Griffon 2.0 website&lt;/a>&lt;/li>
&lt;li>Guillaume Laforge mentions a &lt;a href="https://twitter.com/glaforge/status/487323336876515329">dozen pull requests&lt;/a> in a day following the announcement of the beta version of the new Groovy website&lt;/li>
&lt;li>Cédric Champeau published an updated &lt;a href="https://twitter.com/CedricChampeau/status/488708268181954560">Android GR8Conf agenda application built with Groovy&lt;/a> for GR8Conf US 2014&lt;/li>
&lt;li>Cédric Champeau and Robert Fletcher &lt;a href="https://twitter.com/CedricChampeau/status/488708437493420034">contrast Groovy traits and Java 8 default methods&lt;/a>&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/JennStrater/status/488511425548013568">Gr8Ladies website received a nice facelift&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/ratpackweb/status/488650602230980608">Ratpack 0.9.7 will integrate with the Gradle Shadow plugin&lt;/a> for fat-jar-ing Ratpack apps&lt;/li>
&lt;li>Eric MacAdie is using &lt;a href="http://www.macadie.net/2014/07/10/using-groovy-validator-on-immutable-objects/">Groovy Validator on immutable objects&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>Alexander Klein is exploring &lt;a href="https://github.com/karfunkel/grooid-playground">Groovy builders for Android&lt;/a>&lt;/li>
&lt;li>Roberto Guerra shows to &lt;a href="https://github.com/uris77/ratpack-oauth-example">authenticate with via Google OAuth for your Ratpack&lt;/a> applications&lt;/li>
&lt;/ul>
&lt;h2 id="books">Books&lt;/h2>
&lt;ul>
&lt;li>Glen Smith is proud to have the &lt;a href="https://twitter.com/glen_a_smith/status/488602309123772419">Grails in Action 2nd edition book&lt;/a> in his hands for real&lt;/li>
&lt;li>&lt;a href="https://twitter.com/manningbooks/status/487923316318277632">Grails in Action 2nd edition is now available in print&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>The <a href="http://beta.groovy-lang.org/">new Groovy website</a> announced last week keeps on improving, thanks to your feedback and already many contributions through pull requests on Github!</p>
<p>To wet your appetite for GR8Conf US 2014 at the end of the month, we also have more GR8Conf Europe 2014 videos published last week!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://twitter.com/martinlippert/status/487631673836965888">Groovy Grails ToolSuite 3.6.0 out with Groovy 2.3 and Grails 2.4</a> support on Eclipse 4.4</li>
<li><a href="https://twitter.com/grooscript/status/488453854032560128">GrooScript 0.5.2</a> is out</li>
<li>The <a href="http://beta.groovy-lang.org/download.html">Windows installer for Groovy 2.3.4</a> is available thanks to Joachim Baumann</li>
<li>Andrés Almiray released the <a href="https://twitter.com/aalmiray/status/487255344234905600">Asciidoctor Gradle plugin v0.7.3</a> which is compatible with JDK 6+</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Kunal Dabir created an “<a href="https://github.com/kdabir/awesome-groovy">awesome-groovy</a>” Github project providing a curated list of interesting Groovy-based projects</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Benjamin Muschko explains <a href="http://www.drdobbs.com/jvm/why-build-your-java-projects-with-gradle/240168608">why build your Java projects with Gradle</a> rather than Ant or Maven</li>
<li>Guillaume Laforge gathered the <a href="https://glaforge.dev/posts/2014/07/13/feedback-and-actions-for-the-new-groovy-website/">feedback the team received about the new Groovy website</a></li>
<li>Roberto Guerra writes about <a href="http://blog.stumblingoncode.com/posts/2014-07-11-ratpack-templates.html">Ratpack&rsquo;s templates</a></li>
<li><a href="http://www.sven-ehrke.de/asciidocblog/switch_dependencies_binary_source.html">Use Gradle to switch a dependency from binary to source</a> mode and back, by Sven Ehrke</li>
<li>Grails Goodness by MrHaki
<ul>
<li><a href="http://mrhaki.blogspot.fr/2014/07/grails-goodness-change-response-formats.html">change response formats in RestfulController</a></li>
<li><a href="http://mrhaki.blogspot.fr/2014/07/grails-goodness-custom-controller-class.html">custom controller class with resource Annotation</a></li>
<li><a href="http://mrhaki.blogspot.fr/2014/07/grails-goodness-using-converter-named.html">using converter named configurations with default renderers</a></li>
</ul>
</li>
<li>Pawel Oczadly reveals a Gradle tip: <a href="http://paweloczadly.github.io/dev/2014/07/09/gradle-set-different-name-for-wrapper-and-default-build-script/">set a different name for wrapper and the default build script</a></li>
</ul>
<h2 id="presentations--gr8conf-europe-2014">Presentations — GR8Conf Europe 2014</h2>
<ul>
<li>Noam Tenne delivered a <a href="https://www.youtube.com/watch?v=GfIhxi7L6R0&amp;feature=youtu.be&amp;a">fantastic Groovy puzzler talk</a></li>
<li>Graeme Rocher talks about <a href="https://www.youtube.com/watch?v=NZH9n3s8Ai0&amp;feature=youtu.be&amp;a">Grails 3 and beyond</a></li>
<li>Andrés Almiray on <a href="https://www.youtube.com/watch?v=hrM_AtD5eCw&amp;feature=youtu.be&amp;a">what&rsquo;s new and coming in Griffon</a></li>
<li>Lari Hotari on <a href="https://www.youtube.com/watch?v=_JVeuC8R5BM&amp;feature=youtu.be&amp;a">Grails 3 and Ratpack</a></li>
<li>Peter Ledbrook guides you through <a href="https://www.youtube.com/watch?v=pzZARB7SxdA&amp;feature=youtu.be&amp;a">bootstrapping your projects with Lazybones</a></li>
<li>Jeff Brown shows you how you can do <a href="https://www.youtube.com/watch?v=L-sH9Bn9y_c&amp;feature=youtu.be&amp;a">polyglot web development with Grails 2</a></li>
</ul>
<h2 id="mailing-list-posts">Mailing-list posts</h2>
<ul>
<li>Dylan Bijnagte announces Spock-Genesis: <a href="http://groovy.329449.n5.nabble.com/ANN-spock-genesis-initial-release-0-1-0-td5720402.html">data generators for data-driven / property based testing with Spock</a></li>
</ul>
<h2 id="google-posts">Google+ posts</h2>
<ul>
<li>LinYuan Zheng launched an effort to <a href="https://plus.google.com/b/113675159854671799959/106541219437002995923/posts/hcKBXa3tSkW?cfem=1">translate the Groovy documentation in Chinese</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Marco Vermeulen is looking for a <a href="https://twitter.com/marcoVermeulen/status/488436820456525824">PaaS sponsor to host GVM</a></li>
<li>Dan Woods measured <a href="https://twitter.com/danveloper/status/488138482280189952">Ratpack&rsquo;s great stable performance at 52K requests/second</a> on a large Amazon EC2 instance</li>
<li>Cédric Champeau notices that in a matter of hours, the new <a href="https://twitter.com/cedricchampeau/status/486627877656752128">Groovy website project on Github received 3 pull requests</a></li>
<li>As noted by Vladimír Oraný, the <a href="https://twitter.com/musketyr/status/486739565265555456">&ldquo;improve the doc&rdquo; button on the new Groovy web site makes it easy to contribute</a> improvements to the site</li>
<li>Andrés Almiray choses <a href="https://twitter.com/aalmiray/status/486951644874432512">JBake and Asciidoctor for the upcoming Griffon 2.0 website</a></li>
<li>Guillaume Laforge mentions a <a href="https://twitter.com/glaforge/status/487323336876515329">dozen pull requests</a> in a day following the announcement of the beta version of the new Groovy website</li>
<li>Cédric Champeau published an updated <a href="https://twitter.com/CedricChampeau/status/488708268181954560">Android GR8Conf agenda application built with Groovy</a> for GR8Conf US 2014</li>
<li>Cédric Champeau and Robert Fletcher <a href="https://twitter.com/CedricChampeau/status/488708437493420034">contrast Groovy traits and Java 8 default methods</a></li>
<li>The <a href="https://twitter.com/JennStrater/status/488511425548013568">Gr8Ladies website received a nice facelift</a></li>
<li><a href="https://twitter.com/ratpackweb/status/488650602230980608">Ratpack 0.9.7 will integrate with the Gradle Shadow plugin</a> for fat-jar-ing Ratpack apps</li>
<li>Eric MacAdie is using <a href="http://www.macadie.net/2014/07/10/using-groovy-validator-on-immutable-objects/">Groovy Validator on immutable objects</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Alexander Klein is exploring <a href="https://github.com/karfunkel/grooid-playground">Groovy builders for Android</a></li>
<li>Roberto Guerra shows to <a href="https://github.com/uris77/ratpack-oauth-example">authenticate with via Google OAuth for your Ratpack</a> applications</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>Glen Smith is proud to have the <a href="https://twitter.com/glen_a_smith/status/488602309123772419">Grails in Action 2nd edition book</a> in his hands for real</li>
<li><a href="https://twitter.com/manningbooks/status/487923316318277632">Grails in Action 2nd edition is now available in print</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Feedback and actions for the new Groovy website</title><link>https://glaforge.dev/posts/2014/07/13/feedback-and-actions-for-the-new-groovy-website/</link><pubDate>Sun, 13 Jul 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/07/13/feedback-and-actions-for-the-new-groovy-website/</guid><description>&lt;p>In this post, I&amp;rsquo;ll sum up the feedback we&amp;rsquo;ve gathered through the mailing-lists, Twitter, Google+, blog comments, Github issues, regarding the release of the beta of the &lt;a href="http://beta.groovy-lang.org/">new Groovy website&lt;/a>.&lt;/p>
&lt;p>Overall, so far, the feedback has been very positive, and people are very excited about the fresher and more modern look of the website, as well as happy to find relevant information more easily in a couple of clicks.&lt;/p>
&lt;p>But like anything, there are various aspects we can improve! Some ideas exposed below were already elements we already had on our roadmap, or on the back-burner as nice-to-have’s, but it’s worth sharing them here and comment on them.&lt;/p></description><content:encoded>
<![CDATA[<p>In this post, I&rsquo;ll sum up the feedback we&rsquo;ve gathered through the mailing-lists, Twitter, Google+, blog comments, Github issues, regarding the release of the beta of the <a href="http://beta.groovy-lang.org/">new Groovy website</a>.</p>
<p>Overall, so far, the feedback has been very positive, and people are very excited about the fresher and more modern look of the website, as well as happy to find relevant information more easily in a couple of clicks.</p>
<p>But like anything, there are various aspects we can improve! Some ideas exposed below were already elements we already had on our roadmap, or on the back-burner as nice-to-have’s, but it’s worth sharing them here and comment on them.</p>
<p>Also worth noting that in less than 12 hours after the announcement of the beta of the new website, we received several contributions on Github for fixing some typos or broken sentences, as well as some new content like a new book in the learn section.</p>
<h2 id="responsive-design">Responsive design</h2>
<p>Although some elements of the design are responsive (with CSS media queries), various aspects of the site are clearly not responsive and don’t degrade well and display nicely on mobile devices like phones and tablets.</p>
<p>For example, the menu doesn’t shrink down into a menu widget like is pretty common nowadays, and it’s probably the reason why the width of text content containers also doesn’t shrink accordingly, letting the text become very small and unreadable on smaller screens.</p>
<h2 id="concrete-feedback-mentioned">Concrete feedback mentioned:</h2>
<ul>
<li>scrolling lagging on mobile safari</li>
<li>from the US the site appeared slow (our hoster is in Germany)</li>
<li>text too small on mobile devices</li>
<li>not fully responsive</li>
</ul>
<p>Help needed from frontend-savy persons to help us:</p>
<ul>
<li>improve the responsiveness of the screen for mobile devices</li>
<li>optimize the size so it loads and renders faster</li>
</ul>
<p>Actions:</p>
<ul>
<li>we can cache the fonts coming from Google Fonts and serve them from the website</li>
<li>decrease the size of pictures</li>
<li>use sprites for the logos (“they use Groovy”)</li>
</ul>
<h2 id="content-improvements">Content improvements</h2>
<p>Here are some suggestions for content that users would like to see.</p>
<h3 id="getting-started-guide">Getting started guide</h3>
<p>We’re missing a “getting started guide” that would take users through the steps to download and install Groovy, and then showing them the basics of the language.</p>
<p>We could perhaps integrate a guide like the <a href="http://gr8labs.org/getting-groovy/">getting started guide</a> from MrHaki.</p>
<p>Additionally, it would be nice if the samples in such a guide could be edited live, or played with by the readers. See next section on this topic.</p>
<p>Actions:</p>
<ul>
<li>write a getting started guide distinct from the reference documentation for a fast-track learning approach</li>
</ul>
<p>Possible improvements:</p>
<ul>
<li>The website could also provide some introductions to the 6 key areas listed, that we could link directly from the front page:
<ul>
<li>“flat learning curve” — mention how it’s familiar to Groovy developers</li>
<li>“smooth Java integration” — mention how you can mix Groovy and Java together</li>
<li>“vibrant and rich ecosystem” — perhaps just link to the “ecosystem” section</li>
<li>“powerful feature” — perhaps lead to the “getting started guide” that would give a quick intro to those powerful features</li>
<li>“domain-specific languages” — perhaps a link to the DSL user guide from the reference documentation or a simpler getting started page on the topic</li>
<li>“scripting &amp; test glue” — show some samples of how to use Groovy for command-line scripting / automation, and samples of readable tests (likely Spock)</li>
</ul>
</li>
</ul>
<h3 id="interactive-code-samples">Interactive code samples</h3>
<p>We received several related comments regarding the fact we’re missing code samples (apart from inside the documentation of course):</p>
<ul>
<li>show code samples from the front page to show readers what Groovy looks like</li>
<li>some kind of built-in terminal or shell where we could play with Groovy code in a sandbox in the browser</li>
<li>being able to run the code samples we display</li>
</ul>
<p>Actions:</p>
<ul>
<li>add code sample on the front page</li>
<li>add a built-in shell</li>
</ul>
<h2 id="missing-blog">Missing blog</h2>
<p>Currently, we don’t yet have a news or blog section in the website, as we haven’t yet decided how we would integrate that in the website. It’s important to be able to announce new releases, interesting articles, etc, to our community, beyond the mailing-lists or the social networks through which we usually communicate.</p>
<p>Actions:</p>
<ul>
<li>decide what technical approach to take to offer news and posts in the website</li>
<li>implement / integrate the desired blogging approach</li>
</ul>
<p>Possible future improvements:</p>
<ul>
<li>also integrate the Groovy Weekly news as blog posts</li>
</ul>
<h2 id="user-groups">User groups</h2>
<p>The Groovy user groups are clearly an important part of the success of Groovy and its community. The website currently doesn’t have a user group section as initially planned.</p>
<p>Actions:</p>
<ul>
<li>add a new section in the community about user groups (2014-07-10)</li>
</ul>
<p>Possible future improvements:</p>
<ul>
<li>show an interactive map of the location of the various groups</li>
<li>let user groups contribute calendar events that could be displayed in the events page</li>
</ul>
<h2 id="sponsors-not-listed">Sponsors not listed</h2>
<p>In the old website, we had some places where we could mention our sponsors, but the new website currently doesn’t mention them.</p>
<p>Existing sponsors of the project:</p>
<ul>
<li>Pivotal — hire several Groovy core team members<br />
The Pivotal logo is already present in the design, in the footer of the page.</li>
<li>JetBrains — provide us with free TeamCity and IntelliJ IDEA Ultimate licenses, and cover the cost of our server (hosting the CI server, the website, and the documentation)</li>
<li>JFrog — provide the Bintray and Artifactory OSS infrastructure for deploying our releases and snapshots</li>
</ul>
<p>Actions:</p>
<ul>
<li>see how we can integrate our sponsors, in the footer or in a dedicated section (2014-07-13)</li>
</ul>
<h2 id="videos">Videos</h2>
<p>Interesting videos related to the Groovy project have been recorded at conferences like GR8Conf, Greach, SpringOne2GX and other Java-related conferences. Such videos could also be a key asset in the learning experience of Groovy users.</p>
<p>Actions:</p>
<ul>
<li>add a video section in the learning area of the website</li>
</ul>
<h2 id="other-ecosystem-projects">Other ecosystem projects</h2>
<p>The “ecosystem” page currently lists just a handful of well-known Groovy ecosystem projects. Other module / projects authors would be interested in appearing in that section as well. But the problem (in the old website for instance) is that often several small projects are not really alive anymore, and it gives a bad feeling about the state of the community overall.</p>
<p>Actions:</p>
<ul>
<li>see how we can get additional / lesser-known projects up in the community section, perhaps with another page for contributed projects</li>
</ul>
<p>Possible improvements:</p>
<ul>
<li>see if we can integrate with an API like Ohloh’s which provide an indicator of project “health”, which could allow us to automatically list / remove projects which are not deemed healthy or need some more love</li>
</ul>
<p>There are various indicators that we can take into account for evaluating the liveliness of a project:</p>
<ul>
<li>its Ohloh health status mentioned above</li>
<li>the date of the latest commits</li>
<li>the date of the latest release</li>
<li>recent successful Travis / Jenkins / other CI build status</li>
</ul>
<h2 id="translations">Translations</h2>
<p>Several persons mentioned their desire to have the documentation translated in other languages, and some even proposed their help.</p>
<p>On the technical side, we haven’t put anything in place neither for the website nor for the documentation to be able to provide translated website and documentation.</p>
<p>On the human side, I know it’s already difficult to even just finish the documentation, that I’m a bit doubtful that we will ever get to a point where all the documentation is also translated, and in “sync” with the English base documentation. Some efforts have been made historically on the Groovy website or Grails documentation, but none ever got to completion.</p>
<p>Possible future actions:</p>
<ul>
<li>investigate how we can provide the technical infrastructure for authoring translations and publish them</li>
</ul>
<h2 id="usability">Usability</h2>
<p>Apart from the important responsiveness aspect we’ve outlined already, there are other areas where we can improve the usability of the website.</p>
<h2 id="subdomain-usage">Subdomain usage</h2>
<p>Currently, the website is in beta mode under the “beta” subdomain, and the documentation is also available through that subdomain as well as under “docs”.</p>
<p>Actions:</p>
<ul>
<li>once the website exits the beta status, we’ll be able to have dedicated “www” and “docs” subdomains to clearly demarcate website and documentation (which is important for search, see below)</li>
</ul>
<h2 id="search">Search</h2>
<p>The search is powered by Google Custom Search and is even faceted so that users can search only mailing-list posts, JIRA issues, documentation, etc.</p>
<p>The faceted aspect can be improved a bit when we properly separate and finalize the subdomains, with “www” for the website and “docs” for the reference documentation.</p>
<p>Beside that, the main gripe is the fact that when a user clicks on a search result, the browser moves to that result page, but when the user tracks back to the previous page, the results are gone, showing blank search field and results — ie. the search results are lost.</p>
<p>Actions:</p>
<ul>
<li>use target _blank to open a new tab in the browser for the search results (2014-07-13)</li>
</ul>
<h2 id="ui-inconsistencies">UI inconsistencies</h2>
<p>The website and the reference documentation share a similar look’n feel, with the same fonts and colors, but there are still differences like the color of the links, the font size, the spacing between elements, etc.</p>
<p>Action:</p>
<ul>
<li>futher work to homogenized both the website and the reference documentation</li>
</ul>
<h2 id="miscellaneous">Miscellaneous</h2>
<p>Other feedback we’ve received and other ideas but not listed in the previous sections:</p>
<ul>
<li>The “definition” of what Groovy is might perhaps be a bit too long or complicated, as Groovy is by nature a “multi-faceted” language with different flavors (static / dynamic / optional typing, and OO / imperative / functional language)</li>
<li>In the “they use Groovy” section</li>
<li>EADS is now actually the Airbus Group, so the logo could be changed (2014-07-10)</li>
<li>Best Buy are heavy users of Groovy and their logo could be added (2014-07-10)</li>
<li>Suggestion to use a different domain name, with .io, without dashes, etc.</li>
<li>Add a sitemap.xml file to help with search engine indexing</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #29</title><link>https://glaforge.dev/posts/2014/07/08/groovy-weekly-29/</link><pubDate>Tue, 08 Jul 2014 03:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/07/08/groovy-weekly-29/</guid><description>&lt;p>Keywords for today: beta Groovy website, Gradle roadmap, GR8Conf presentations!&lt;/p>
&lt;p>It’s a launch day! The launch of the beta of the Groovy website, mentioned in the news section (and also a bug fix release with Groovy 2.3.4).&lt;/p>
&lt;p>In the article section, you’ll find the link to Hans Dockter’s latest post on the Gradle forums which details what you can expect from future Gradle versions, and it’s very promising: think performance, parallelization, caching, sharing and tooling!&lt;/p></description><content:encoded>
<![CDATA[<p>Keywords for today: beta Groovy website, Gradle roadmap, GR8Conf presentations!</p>
<p>It’s a launch day! The launch of the beta of the Groovy website, mentioned in the news section (and also a bug fix release with Groovy 2.3.4).</p>
<p>In the article section, you’ll find the link to Hans Dockter’s latest post on the Gradle forums which details what you can expect from future Gradle versions, and it’s very promising: think performance, parallelization, caching, sharing and tooling!</p>
<p>In the presentation section, the GR8Conf Europe crew has been editing and publishing the videos of presentations given at last month conference in Denmark. So you have a few hours worth of Groovy content to watch! Also for those in the US, don’t forget that GR8Conf US is fast approaching, at the end of the month!</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Guillaume Laforge announces the <a href="https://glaforge.dev/posts/2014/07/08/groovy-2-3-4-is-out/">release of Groovy 2.3.4</a>, with further bug fixes in the anonymous inner class support and also compatibility between AST transformations and static compilation</li>
<li>The <a href="http://docs.codehaus.org/display/GRIFFON/2014/07/03/Griffon+2.0.0.BETA3+Released">third beta of Griffon 2.0</a> is out</li>
<li><a href="https://spring.io/blog/2014/07/08/spring-boot-1-1-4-released">Spring Boot 1.1.4</a> released</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Guillaume Laforge announces the <a href="https://glaforge.dev/posts/2014/07/08/a-new-groovy-website-in-beta/">beta of the new Groovy website</a>, a project hosted on Github people can contribute to, built with Groovy and Gradle</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Hans Dockter reveals the upcoming plans for the <a href="http://forums.gradle.org/gradle/topics/revolutionary_new_gradle_features_on_the_2014_roadmap">revolutionary features on the Gradle roadmap</a></li>
<li>Robert Fletcher explains one of the <a href="http://blog.freeside.co/post/91127019156/compilestatic-and-polymorphic-method-dispatch">subtle differences in behavior between &ldquo;dynamic Groovy&rdquo; and statically compiled Groovy</a> code in the case of multi-methods</li>
<li>Mark Perry&rsquo;s ongoing functional explorations with <a href="http://mperry.github.io/2014/07/02/groovy-applicatives.html">Groovy applicatives</a></li>
<li>Andrés Almiray explains how to <a href="http://www.jroller.com/aalmiray/entry/getting_started_with_griffon_and">get started with Griffon and JavaFX</a></li>
<li>Andrés Almiray delivers the second part of <a href="http://www.jroller.com/aalmiray/entry/getting_started_with_griffon_and1">Griffon and JavaFX</a> article</li>
<li>Pawel Oczadly details how to <a href="http://paweloczadly.github.io/dev/2014/07/03/gradle-how-to-use-variables-and-methods-from-another-gradle-file/">use variables and methods from another Gradle build</a> file</li>
<li>MrHaki&rsquo;s Grails Goodness: <a href="http://mrhaki.blogspot.fr/2014/07/grails-goodness-enable-accept-header.html">enable accept header for user agent requests</a></li>
</ul>
<h2 id="presentations--gr8conf-europe-2014-videos">Presentations — GR8Conf Europe 2014 videos</h2>
<ul>
<li>Alexander Klein on <a href="https://twitter.com/gr8conf/status/486086506608459780">Vert.x using Groovy</a></li>
<li>Andrés Almiray gives an <a href="https://twitter.com/gr8conf/status/485050696236929024">overview of the Groovy Ecosystem</a></li>
<li>Peter Ledbrook talking about <a href="https://twitter.com/gr8conf/status/485044283917885440">application architectures with Grails</a></li>
<li>Jeff Brown talks about <a href="https://twitter.com/gr8conf/status/486085532137426944">metaprogramming with the Groovy runtime</a> (part 1 of 2)</li>
<li>Jeff Brown&rsquo;s second part on <a href="https://twitter.com/gr8conf/status/486085531193708544">metaprogramming with the Groovy runtime</a> (part 2 of 2)</li>
<li>Marco Vermeulen on <a href="https://twitter.com/gr8conf/status/486425245717233664">BDD using Groovy and Cucumber</a></li>
<li>Grails and Devops, <a href="https://twitter.com/gr8conf/status/486424949058322432">continuous integration and delivery in the cloud</a> by Benoît Hédiard</li>
<li>Guillaume Laforge on <a href="https://twitter.com/gr8conf/status/485475403864104960">Groovy in 2014 and beyond</a></li>
<li>Robert Fletcher demonstrates <a href="https://twitter.com/gr8conf/status/485335393315717120">hybrid client/server view rendering with Grails</a></li>
<li>Claus Ibsen on <a href="https://twitter.com/gr8conf/status/485166038510620672">integration with Apache Camel and Groovy</a></li>
<li>Jorge Franco Leza delves into <a href="https://twitter.com/gr8conf/status/485160698205327360">GrooScript, the Groovy to JavaScript transpiler</a></li>
</ul>
<h2 id="google-post">Google+ post</h2>
<ul>
<li>Guillaume Barthe announces a <a href="https://plus.google.com/u/0/b/100737441520485064384/100737441520485064384/posts">Google+ page dedicated to French-speaking Groovy and Grails users</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Cédric Champeau reveals that the <a href="https://twitter.com/cedricchampeau/status/486583986979090432">new Groovy website is making use of Groovy&rsquo;s new markup template engine</a></li>
<li><a href="https://twitter.com/ratpackweb/status/484289839500513280">Ratpack 0.9.7 will support Groovy 2.3&rsquo;s markup template engine</a></li>
<li>In its latest update, Apple updated the range operator of the <a href="https://twitter.com/practicalswift/status/486479638760140800">Swift language to borrow again from Groovy with the ..&lt; notation</a> for ranges with excluded upper bound</li>
<li>Eric MacAdie works on a <a href="http://www.macadie.net/2014/07/02/more-on-groovy-validation/">Groovy validator project</a></li>
<li>Cédric Champeau <a href="https://twitter.com/cedricchampeau/status/485360654266945536">updated the GR8Conf Agenda Groovy / Android application</a> to work with GR8Conf US 2014</li>
<li>The new Groovy documentation is built with Asciidoctor, and Dan Allen shows how this <a href="https://twitter.com/mojavelinux/status/485197497594171392">Groovy documentation can be rendered in ePub format</a></li>
<li>Yoav Landman mentions <a href="https://twitter.com/_yoav_/status/485185270283657216">enhancements in the Gradle Bintray plugin</a> that will help auto-inclusion of Gradle plugins in the Gradle plugin portal</li>
<li><a href="https://twitter.com/gvmtool/status/486585262580195328">Spring Boot 1.1.4</a> available in GVM</li>
<li><a href="https://twitter.com/gvmtool/status/486584287689719810">Groovy 2.3.4</a> is available through GVM</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Paul Osborne shows a nice usage of <a href="https://twitter.com/posbornet/status/484294950772023296">multiple assignment to split a date string</a> and fetch its mont and year elegantly</li>
<li>Andrés Almiray <a href="https://twitter.com/aalmiray/status/484778706339381248">contrasts a Griffon / JavaFX application written in Groovy and in Java</a></li>
<li>A new example <a href="https://github.com/dbirtwell/trackmystuff_ratpack_with_db">Ratpack application using GStorm for persistence</a> by David Birtwell</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>More teasers for the <a href="https://storify.com/glaforge/more-gr8conf-us-teasers">upcoming presentations at GR8Conf US 2014</a>, with new talks announced</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.3.4 is out</title><link>https://glaforge.dev/posts/2014/07/08/groovy-2-3-4-is-out/</link><pubDate>Tue, 08 Jul 2014 02:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/07/08/groovy-2-3-4-is-out/</guid><description>&lt;p>We&amp;rsquo;re happy to announce the bug-fix release of Groovy 2.3.4.&lt;/p>
&lt;p>In store, we continued on our hunt of bugs related to anonymous inner classes, and we have a big work on the compatibility of our AST transformations with static compilation.&lt;br />
&lt;a href="http://beta.groovy-lang.org/download.html">Download Groovy 2.3.4&lt;/a> from our &lt;a href="http://beta.groovy-lang.org/">new website&lt;/a>:&lt;/p>
&lt;p>You can read the &lt;a href="https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;version=20432">JIRA release notes&lt;/a>.&lt;/p>
&lt;p>Thanks to all those who contributed to this release!&lt;/p>
&lt;p>Keep on Groovy-ing!&lt;/p></description><content:encoded>
<![CDATA[<p>We&rsquo;re happy to announce the bug-fix release of Groovy 2.3.4.</p>
<p>In store, we continued on our hunt of bugs related to anonymous inner classes, and we have a big work on the compatibility of our AST transformations with static compilation.<br />
<a href="http://beta.groovy-lang.org/download.html">Download Groovy 2.3.4</a> from our <a href="http://beta.groovy-lang.org/">new website</a>:</p>
<p>You can read the <a href="https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=20432">JIRA release notes</a>.</p>
<p>Thanks to all those who contributed to this release!</p>
<p>Keep on Groovy-ing!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A new Groovy website in beta</title><link>https://glaforge.dev/posts/2014/07/08/a-new-groovy-website-in-beta/</link><pubDate>Tue, 08 Jul 2014 01:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/07/08/a-new-groovy-website-in-beta/</guid><description>&lt;p>The past few weeks, the Groovy team has been working on a new website for the project.&lt;/p>
&lt;p>Without further ado, let me introduce you to its beta: &lt;a href="http://beta.groovy-lang.org">http://beta.groovy-lang.org&lt;/a>.&lt;/p>
&lt;p>The website is actually a &lt;a href="https://github.com/groovy/groovy-website">Groovy and Gradle application that you can fork&lt;/a> and help us improve! So don&amp;rsquo;t hesitate to contribute fixes for things like typos or broken English, or suggest new relevant sections, etc. Your help will be welcome. Notice the &amp;ldquo;improve this doc&amp;rdquo; buttons on all pages which lead you to the relevant Github page that you can edit live, inline, on Github (if you&amp;rsquo;ve got an account already).&lt;/p></description><content:encoded>
<![CDATA[<p>The past few weeks, the Groovy team has been working on a new website for the project.</p>
<p>Without further ado, let me introduce you to its beta: <a href="http://beta.groovy-lang.org">http://beta.groovy-lang.org</a>.</p>
<p>The website is actually a <a href="https://github.com/groovy/groovy-website">Groovy and Gradle application that you can fork</a> and help us improve! So don&rsquo;t hesitate to contribute fixes for things like typos or broken English, or suggest new relevant sections, etc. Your help will be welcome. Notice the &ldquo;improve this doc&rdquo; buttons on all pages which lead you to the relevant Github page that you can edit live, inline, on Github (if you&rsquo;ve got an account already).</p>
<p>This initial design prototype was created by <a href="https://twitter.com/oodamien">Damien Vitrac</a>, who also designed the <a href="https://grails.org/">Grails</a> website. Big thanks to Damien for this great work!</p>
<p>The site is currently in beta, awaiting your feedback, and some further minor improvements. You will notice also some links in the documentation leading to 404s as we still have Asciidoctor documentation to write. This is also an area where authors are welcome to give a hand; if there are particular topics you&rsquo;d like to work on, please raise your hand.</p>
<p>Once we&rsquo;re happy with the state of this website, and also with the related documentation that still needs to be written and linked to, we&rsquo;ll be switching from beta to official mode, and <a href="https://www.groovy-lang.org">www.groovy-lang.org</a> will become the new home for the Groovy project. The old wiki documentation from Confluence will be frozen and available in PDF form should you need it.</p>
<p>We&rsquo;re looking forward to your feedback, your suggestions, and more!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #28</title><link>https://glaforge.dev/posts/2014/07/01/groovy-weekly-28/</link><pubDate>Tue, 01 Jul 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/07/01/groovy-weekly-28/</guid><description>&lt;p>A big week for the Gradle team as they’ve just announced the final release of Gradle 2.0! It’s of course a very important milestone for the project, and it’s nice to see that the migration to Groovy 2.3 as a baseline.&lt;/p>
&lt;p>There are also some updates to Grails, Ratpack, and also the launch of the Spring IO platform that you might be interested in.&lt;/p>
&lt;p>You’ll see also a list of job postings for Netflix, where Groovy is deployed at a very large scale and who are always seeking Groovy talents.&lt;/p></description><content:encoded>
<![CDATA[<p>A big week for the Gradle team as they’ve just announced the final release of Gradle 2.0! It’s of course a very important milestone for the project, and it’s nice to see that the migration to Groovy 2.3 as a baseline.</p>
<p>There are also some updates to Grails, Ratpack, and also the launch of the Spring IO platform that you might be interested in.</p>
<p>You’ll see also a list of job postings for Netflix, where Groovy is deployed at a very large scale and who are always seeking Groovy talents.</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://forums.gradle.org/gradle/topics/gradle_2_0_released">Gradle 2.0</a> released (with Groovy 2.3 included!)</li>
<li><a href="http://www.ratpack.io/versions/0.9.6">Ratpack 0.9.6</a> released</li>
<li><a href="https://grails.org/news/1290721">Grails 2.4.2 and 2.3.11</a> are released</li>
<li>The GrooScript Gradle pluging v0.4 is out, with Gradle 2.0 and Groovy 2.3.3</li>
<li>The <a href="https://spring.io/blog/2014/06/26/spring-io-platform-1-0-0-released">Spring IO platform 1.0</a> has just been released, aligned with the recent Groovy &amp; Grails releases</li>
<li><a href="https://twitter.com/pledbrook/status/481773978206601217">Lazybones 0.7</a> is released</li>
<li><a href="https://twitter.com/springboot/status/481569291754434560">Spring Boot 1.1.2</a> is out</li>
<li><a href="https://spring.io/blog/2014/06/27/spring-boot-1-1-3-available-now">Spring Boot 1.1.3</a> out as well!</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>June <a href="http://www.gradleware.com/newsletter/gradleware-newsletter-june-2014/">newsletter from GradleWare</a> about all things Gradle</li>
<li><a href="http://mperry.github.io/2014/07/01/groovy-functors.html">Functors in Groovy</a> by Mark Perry</li>
<li>MrHaki&rsquo;s Spocklight: <a href="http://mrhaki.blogspot.fr/2014/06/spocklight-assign-multiple-data.html">assign multiple data variables from provider</a></li>
<li>MrHaki shows how to <a href="http://mrhaki.blogspot.fr/2014/06/spocklight-write-our-own-data-provider.html">write your own data providers for Spock</a></li>
<li>Infographic article from Cygnet Infotech on <a href="http://www.cygnet-infotech.com/infographics/legacy-system-to-grails">moving from legacy technologies towards Grails</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Dan Woods presented <a href="https://github.com/danveloper/uberconf2014-ratpack">Ratpack</a> at UberConf 2014, with slides and demos</li>
<li>Slides and demos from Dan Woods on <a href="https://github.com/danveloper/uberconf2014-from-groovy-to-java8">introducing Groovy developers to the Java 8</a> new features, presented at UberConf 2014</li>
<li><a href="https://speakerdeck.com/jlstrater/groovy-as-a-scripting-language">Groovy as a scripting language</a>, by Jen Strater, at the GR8Ladies GR8Workshop</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>The <a href="http://groovyfx.org/docs/index.html">GroovyFX project migrated its documentation to Asciidoctor</a></li>
<li><a href="https://twitter.com/rfletcherew/status/482479225253810176">Intellij IDEA&rsquo;s new Early Access Program</a> has started, for version 14, which includes the Groovy traits support, as well as better interactions between Grails and Gradle</li>
</ul>
<h2 id="mailing-lists">Mailing-lists</h2>
<ul>
<li>Cédric Champeau announced that <a href="http://groovy.329449.n5.nabble.com/Docs-available-for-older-versions-of-Groovy-td5720254.html">older versions of the Groovy documentation</a> were now also available online</li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li><a href="https://twitter.com/tmbradley73/status/481824337343684608">Netflix is still looking for Groovy talents</a></li>
<li>Daniel Jacobson from Netflix lists some of the <a href="https://gist.github.com/glaforge/e1d88601c053611678ae">job postings from Netflix where Groovy skills are needed</a></li>
</ul>
<h2 id="podcasts">Podcasts</h2>
<ul>
<li>Peter Ledbrook recoreded <a href="http://groovypodcast.podbean.com/e/groovy-podcast-ep-2-1403795851/">Episode 4 of the Groovy podcast</a>, including an interview with Baruch Sadogursky of JFrog, to discuss Groovy, Gradle, Bintray and more</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Cédric Champeau points at the <a href="https://twitter.com/cedricchampeau/status/482045140547428352">Groovy support for Android</a>, for those who are disappointed that Google didn&rsquo;t announce a new language for Android</li>
<li><a href="https://twitter.com/gvmtool/status/482131395419308032">Grails 2.4.2 and 2.3.11</a> are available on GVM</li>
<li>Rob Fletcher shares a screenshot of the nice <a href="https://twitter.com/rfletcherew/status/482498222074912769">T icon for traits in IntelliJ IDEA 14</a> EAP</li>
<li><a href="https://twitter.com/gvmtool/status/481767669361045504">Lazybones 0.7</a> is available on GVM</li>
<li><a href="https://twitter.com/gvmtool/status/481767515564281856">Spring Boot 1.1.2</a> available through GVM</li>
<li>Alexander Klein explains the <a href="https://storify.com/glaforge/alexander-klein-explains-the-inception-of-the-gvm">inception of the GVM logo</a></li>
<li>A <a href="https://twitter.com/ratpackweb/status/482242234029318145">Ratpack tip</a> to enable static compilation of Ratpack scripts</li>
<li>More <a href="https://twitter.com/joanzap/status/483635064224624641">happy users of Groovy on Android</a>!</li>
<li><a href="https://twitter.com/gvmtool/status/483941783811129344">Gradle 2.0</a> is available through GVM</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Jon Brisbin is brainstorming some a <a href="https://twitter.com/j_brisbin/status/482942386843041793">Basho WebMachine Groovy</a> approach</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="https://storify.com/glaforge/gr8conf-us-programme-and-last-day-for-the-early-bi">GR8Conf US programme, sponsors, and teasing the great talks</a> you&rsquo;ll be able to attend</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #27</title><link>https://glaforge.dev/posts/2014/06/24/groovy-weekly-27/</link><pubDate>Tue, 24 Jun 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/06/24/groovy-weekly-27/</guid><description>&lt;p>This week, I’d like to highlight how you can contribute to the Groovy project!&lt;/p>
&lt;p>The Groovy core team is a very small team, compared to the huge team a company like Oracle puts behind Java and the JVM, or Microsoft behind its languages and the .Net platform. So all contributions, in any form, count, and are important to the success and evolution of the Groovy language.&lt;/p>
&lt;p>If you want to contribute to the code of Groovy, Cédric Champeau recorded a &lt;a href="http://melix.github.io/blog/2014/06/contribute-groovy-ide.html">screencast showing how you can set up IntelliJ IDEA to be able to work on the Groovy codebase&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>This week, I’d like to highlight how you can contribute to the Groovy project!</p>
<p>The Groovy core team is a very small team, compared to the huge team a company like Oracle puts behind Java and the JVM, or Microsoft behind its languages and the .Net platform. So all contributions, in any form, count, and are important to the success and evolution of the Groovy language.</p>
<p>If you want to contribute to the code of Groovy, Cédric Champeau recorded a <a href="http://melix.github.io/blog/2014/06/contribute-groovy-ide.html">screencast showing how you can set up IntelliJ IDEA to be able to work on the Groovy codebase</a>.</p>
<p>But another key area we’ve been working on for a while is the new Groovy documentation. Peter Ledbrook <a href="http://www.cacoethes.co.uk/blog/groovyandgrails/contributing-to-the-groovy-documentation">detailed how you can help us author some sections of the documentation</a>. Like Peter, you can dedicate a bit of your time on a topic of your choice, to further deepen your understanding of a particular area of Groovy and share your knowledge with your peers.</p>
<p>We’re really looking forward for your contributions!</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Szczepan Faber announces <a href="http://forums.gradle.org/gradle/topics/we_need_your_help_trying_out_gradle_2_0_rc_2?rfm=1">Gradle 2.0 rc-2</a></li>
<li><a href="http://docs.codehaus.org/display/GRIFFON/2014/06/20/Griffon+2.0.0.BETA2+Released">Griffon 2.0.0 beta-2</a> released</li>
<li>An update release of all the <a href="https://twitter.com/theaviary/status/480697869767569408">Griffon 2.0.0 beta-3 lazybones templates</a></li>
<li><a href="https://groups.google.com/forum/?fromgroups#!topic/vertx/BFOjtqKeIjI">Vert.x 2.1.1</a> released</li>
<li><a href="https://twitter.com/mrundberget/status/479693228015644672">Bug fix release of the Light Table Groovy</a> client with v0.0.7</li>
<li>Peter Ledbrook released <a href="https://twitter.com/pledbrook/status/479886014085734400">v1.1 of the Lazybones Gradle plugin</a></li>
<li><a href="https://twitter.com/AndreyHihlovski/status/481088938124509184">Gretty 1.0</a> is out, with Tomcat 7 &amp; 8 support</li>
<li>Yoav Landman announces <a href="https://twitter.com/_yoav_/status/481202023258288130">version 0.4 of the Gradle Bintray plugin</a></li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Peter Ledbrook explains how to <a href="http://www.cacoethes.co.uk/blog/groovyandgrails/contributing-to-the-groovy-documentation">contribute to the Groovy documentation</a></li>
<li>A <a href="http://thejavatar.com/testing-with-spock/">detailed introduction to the Spock</a> testing framework by Lukasz Janicki</li>
<li>Dan Woods explores <a href="http://www.infoq.com/articles/microframeworks1-spring-boot">micro-services with Spring Boot and Groovy</a></li>
<li>Alex Staveley writes about <a href="http://dublintech.blogspot.ie/2014/06/mongodb-and-grails.html">MongoDB and Grails</a></li>
<li>Further <a href="http://codewader.blogspot.no/2014/06/a-groovy-light-table-client-step-5.html">Gradle dependecy visualization in Light Table</a>&rsquo;s Groovy client with Dagre-D3 by Magnus Rundberget</li>
<li>MrHaki&rsquo;s Spocklight: <a href="http://mrhaki.blogspot.fr/2014/06/spocklight-ignore-specifications-based.html">ignore specifications based on conditions</a></li>
<li>Java build tools: <a href="http://www.javacodegeeks.com/2014/06/java-build-tools-ant-vs-maven-vs-gradle.html">Ant vs Maven vs Gradle</a>, by Viktor Farcic</li>
<li><a href="http://lhuet.github.io/blog/2014/06/gvm-on-beaglebone.html">Groovy, Gradle and Vert.x on Beaglebone with GVM</a> by Laurent Huet</li>
<li>Part 2 of <a href="http://www.intelligrape.com/blog/2014/06/22/oauth-2-0-using-grails-part-2/">OAuth 2 using Grails</a></li>
<li>A <a href="http://www.objectpartners.com/2014/06/18/gradle-summit-2014-recap/">recap of the Gradle Summit 2014</a> by John Engelman</li>
<li>Robert Fletcher writes about how to use <a href="http://blog.freeside.co/post/89759686171/gradle-and-groovys-invoke-dynamic-support">Groovy&rsquo;s invoke dynamic support in your Gradle projects</a></li>
<li>Robert Fletcher explicits how to <a href="http://blog.freeside.co/post/89760608881/using-groovys-invokedynamic-support-in-gradle">trigger the usage Groovy&rsquo;s invoke dynamic support in Gradle</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>The <a href="http://www.cloudbees.com/sites/default/files/juc/juc2014/boston/2014-0618-Boston-Jesse_Glick-Workflow.pdf">Jenkins Workflow projects is built on Groovy</a>, for defining continous delivery pipelines and more, presentation given by Jesse Glick at the Jenkins User Conference Boston</li>
<li>Andrés Almiray <a href="http://cds.cern.ch/record/1709713">presents the Griffon desktop framework at the CERN</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li><a href="http://grydeske.net/news/show/51">Grails Diary week 25</a> by Jacob Aae Mikkelsen</li>
<li>A <a href="http://plugins.jetbrains.com/plugin/7442?pr=idea">GMavenPlus plugin for IntelliJ IDEA</a></li>
<li>Magnus Rundberget points us at the <a href="https://github.com/rundis/LightTable-Groovy/wiki/Contribute">contribution page</a>, if you want to help improve the LightTable Groovy client</li>
<li>The new <a href="http://griffon.github.io/griffon/guide/">Griffon user guide is adopting a similar look&rsquo;n feel and style as the new Groovy documentation</a>, with Asciidoctor</li>
</ul>
<h2 id="screencasts">Screencasts</h2>
<ul>
<li>Cédric Champeau recorded a <a href="http://melix.github.io/blog/2014/06/contribute-groovy-ide.html">screencast showing how to setup IntelliJ IDEA to contribute to the Groovy project</a></li>
</ul>
<h2 id="podcasts">Podcasts</h2>
<ul>
<li>Peter Ledbrook recorded the <a href="https://twitter.com/pledbrook/status/479702066689224704">Groovy podcast episode 3</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Already <a href="https://twitter.com/cedricchampeau/status/479160332049727488">100 stars on Github for the Groovy Android plugin for Gradle</a>. You can still add your own star!</li>
<li>Robert Fletcher shares a Gradle tip on how to <a href="https://twitter.com/rfletcherew/status/479909583595790336">enable the invoke dynamic support in your Gradle build</a> when using the Groovy &ldquo;indy&rdquo; JAR</li>
<li><a href="https://twitter.com/gvmtool/status/479525410561335297">Grails 2.4.1 and 2.3.10</a> available in GVM</li>
<li>Andrés Almiray points out that <a href="https://twitter.com/aalmiray/status/479343529500872704">Griffon 2.0.0 application can be built with Maven</a></li>
<li><a href="https://twitter.com/gvmtool/status/481140386963132416">Gradle 2.0 rc-2</a> is available through GVM</li>
<li><a href="https://twitter.com/gvmtool/status/481161262626504705">GVM has a logo</a>!</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Guillaume Laforge shows a <a href="http://groovyconsole.appspot.com/script/5643440998055936">code visitor that transforms BigDecimal literals into doubles</a></li>
<li>A tiny <a href="http://groovyconsole.appspot.com/script/5653164804014080/">wiki text converter using Groovy traits</a></li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li><a href="https://twitter.com/kenkousen/status/481227428124057601">44% off on Making Java Groovy</a> by Ken Kousen</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>Guillaume Laforge shares the list of <a href="https://glaforge.dev/posts/2014/06/20/groovy-related-talks-at-javaone-2014/">Groovy related talks at JavaOne</a></li>
<li>Only one week left till the <a href="https://twitter.com/gr8confus/status/481089948951781377">early bird pricing end of GR8Conf US</a>. Be fast!</li>
<li><a href="http://springone2gx.com/">Super Early Bird registration for SpringOne2GX</a> ends on June 30th</li>
<li>JDriven is organizing a <a href="http://www.jdriven.com/seminars#grails">workshop on building REST-ful apps with Grails</a>, on July 2nd (Dutch)</li>
<li>Russ Danner will give a webinar on: <a href="https://spring.io/blog/2014/06/12/webinar-conquering-content-enabled-web-and-mobile-applications-with-spring-and-groovy">conquering content-enabled web and mobile applications with Spring and Groovy</a> on July 29th</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy related talks at JavaOne 2014</title><link>https://glaforge.dev/posts/2014/06/20/groovy-related-talks-at-javaone-2014/</link><pubDate>Fri, 20 Jun 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/06/20/groovy-related-talks-at-javaone-2014/</guid><description>&lt;p>Oracle is publishing the agenda of the upcoming JavaOne 2014 conference. And I&amp;rsquo;d like to highlight the &lt;a href="https://oracleus.activeevents.com/2014/connect/search.ww?eventRef=javaone#loadSearch-event=null&amp;amp;searchPhrase=groovy&amp;amp;searchType=session&amp;amp;tc=0&amp;amp;sortBy=&amp;amp;p=&amp;amp;i(10009)=10111">Groovy presentations&lt;/a> I&amp;rsquo;ve noticed that you might be attending in following if you are in San Francisco:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=5839">Groovy in the Light of Java 8 [CON5839]&lt;/a> With Java 8 out the door, Java developers can at last benefit from the long-awaited lambdas to taste the newly found functional flavor of the language. Streams are there to work more easily and efficiently with heaps of data. Those things are not new to developers acquainted with Groovy. But what is left to Groovy to make it attractive beyond all the aspects in which Java has caught up with Groovy? In this session, a Groovy project lead shows you how Groovy is still relevant in the JVM landscape, how similar or different Java and Groovy can be, how Groovy further improves the developer experience on top of JDK 8, and what Groovy offers beyond Java.&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=5996">Groovy in 2014 and Beyond [CON5996]&lt;/a> With three million downloads in a year, Groovy still clearly leads the pack of alternative languages on the JVM, but it’s not resting on its laurels. The latest Groovy release, 2.3, is chock-full of useful new features and performance improvements. In particular, Groovy now supports the concept of “traits” for elegantly composing behaviors. Its JSON support is now the most performant of all the JSON libraries available to date. Groovy 2.3 introduces a new markup-based template engine, new code transformations, and much more. In this session, a Groovy project lead guides you through the latest advancements in the Groovy programming language and tells you what’s cooking for the next releases.&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=1768">Exploring Groovy Metaprogramming [CON1768]&lt;/a> One of the key benefits of Groovy is its metaprogramming capability. The built-in features of the language enable you to realize AOP without the need for any heavyweight tools. This presentation takes an in-depth look at the metaprogramming capabilities of Groovy and explains when to use mixins, method injection, and method synthesis.&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=3538">Functional Programmning the Groovy Way [CON3538]&lt;/a> In recent years, functional programming has gained ground over object-oriented programming, mainly due to the advancement in computing power. The JVM is no exception. You can find powerful contenders in Clojure and Scala, but Groovy is not that far back in the race. The Groovy programming language contains a wide array of APIs and features that facilitate a functional programming style, such as closure composition, memorization, trampolines, and iterator methods. This session explores all of these features that are sure to spice up your daily experience.&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=2425">Rethinking API Design with Groovy Traits [CON2425]&lt;/a> Groovy 2.3 introduces the concept of traits in the language. Traits look like interfaces but enable the developer to add both implementation and state. They introduce multiple inheritance in the language while avoiding the diamond problem. Traits will let you rethink the way you design APIs in Groovy, by favoring composition of behaviors. This session explains what traits are, how they were implemented in Groovy, and what they have to offer to make your code more readable and maintainable.&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=1764">Groovy and Grails Puzzlers: As Usual—Traps, Pitfalls, and End Cases [CON1764]&lt;/a> Remember the epic Java puzzlers? Here’s the Groovy version, and there are some neat ones! Even though it is totally a Grails shop, some of these had JFrog people scratching their head for days, trying to figure them out. And there is more! Contributions from the truly Groovy senseis, including @glaforge, @aalmiray, @tim_yates, and @kenkousen, make this session an unforgettable journey to Groovy’s O_O. You’ll get the expected dose of fun and enlightenment while hearing about mistakes and failures, great and small, in hard-core Groovy/Grails development.&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=1769">Applying Groovy Closures for Fun and Productivity [CON1769]&lt;/a> You can program higher-order functions in Groovy quite easily by using closures, but the benefits of closures go far beyond that. Groovy has a variety of capabilities hidden in closures. This presentation unlocks that treasure and explores ways in which we can design applications by using Groovy closures to apply different design patterns, to create fluent interfaces, and even to program asynchrony.&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=1703">HTML5/AngularJS/Groovy/Java and MongoDB Together: What Could Possibly Go Wrong? [CON1703]&lt;/a> We hear it’s common to create a minimum viable product (MVP) in a language that facilitates rapid prototyping and then migrate to the JVM when the application requires better stability and performance. In this session, the speaker uses Java to create a web application in less than hour. The JVM is a polyglot platform, and you’ll learn how to use the correct tools for this application, including AngularJS, Bootstrap, HTML5, web services, Java, MongoDB, and Groovy—it’s fully buzzword-compliant. The presentation doesn’t go into every technology in depth but demonstrates the role of each tool and how the tools interact. The session will result in a fully working mobile/browser-friendly app without compromise of design or good practice. It’s even going to have tests.&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=1752">Plugging Users In: Extend Your Application with Pluggable Groovy DSL [CON1752]&lt;/a> It is often beneficial to enable users to extend your software with their own logic, and with dynamic languages on the JVM, it is also easy to do so. This session shares JFrog’s experience in creating a public, Groovy-authored user plug-in interface. It explains what domain-specific languages (DSLs) are, what their relevance to user plug-ins is, and how they can be implemented in Groovy or Java. It also discusses another very important aspect of pluggability: good public API design. Further, it covers security concerns and how they should be tackled. And finally, it discusses classpath isolation issues you may run into and compares different solutions to this problem.&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=2939">Script Bowl 2014: The Battle Rages On [CON2939]&lt;/a> In this contest, languages that run on the JVM, represented by their respective language nerds, battle for bragging rights as the most popular language by showing off their shiny new features. Adding a twist to the format in which the audience members will pick this year’s winner(s): they will also vote on a language that should not return in 2015. Returning from 2013 are language gurus representing Clojure, Groovy, JRuby, and Scala. Attend this fun-filled and technically invigorating session to judge which scripting language best meets your needs. You’ll also be able to compare the respective languages and spark some thought-provoking discussions with the panelists that will be beneficial to the entire Java community.&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=3693">Spring 4TW! [CON3693]&lt;/a> Spring 4’s here, and with it comes a bevy of new features designed to support modern web developers as well as enrich the component model and enable even-more-adaptive enterprise applications. Spring 4 also serves as the underpinning of Spring Boot, the entry point into the Spring platform. In this session, a Spring developer advocate discusses Spring 4’s new support for WebSocket (including extra support for STOMP), conditional configuration, dependency injection, Java 8 and Java EE 7 support (JCache support, the Batch JSR, ManagedExecutorService support, and so on), the Groovy language, and more. The presentation also looks at Spring Boot as a productive entry point into the Spring platform.&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=7902">Writing Highly Concurrent Polyglot Applications with Vert.x [CON7902]&lt;/a> Vert.x is a lightweight, high-performance, polyglot, asynchronous application platform for the JVM. This session covers the design principles and motivation behind Vert.x, including its concurrency model, and discusses why Vert.x is a great fit for super-simple, highly concurrent applications. It also dives into some of the key Vert.x features, including the distributed event bus and high availability, and discusses some of the up-and-coming new features in the latest development branch for Vert.x 3.0, including distributed map support, management, and monitoring. The presentation includes demonstrations involving examples in several JVM languages such as Java, JavaScript, Scala, and Groovy.&lt;/li>
&lt;/ul>
&lt;p>Update: I only searched sessions with the &amp;ldquo;groovy&amp;rdquo; keyword, but other Groovy ecosystem projects are also present, like Griffon, Gradle or Spock! So let me fix that by appending some more Groovy-related sessions to my list!&lt;/p></description><content:encoded>
<![CDATA[<p>Oracle is publishing the agenda of the upcoming JavaOne 2014 conference. And I&rsquo;d like to highlight the <a href="https://oracleus.activeevents.com/2014/connect/search.ww?eventRef=javaone#loadSearch-event=null&amp;searchPhrase=groovy&amp;searchType=session&amp;tc=0&amp;sortBy=&amp;p=&amp;i(10009)=10111">Groovy presentations</a> I&rsquo;ve noticed that you might be attending in following if you are in San Francisco:</p>
<ul>
<li><a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=5839">Groovy in the Light of Java 8 [CON5839]</a> With Java 8 out the door, Java developers can at last benefit from the long-awaited lambdas to taste the newly found functional flavor of the language. Streams are there to work more easily and efficiently with heaps of data. Those things are not new to developers acquainted with Groovy. But what is left to Groovy to make it attractive beyond all the aspects in which Java has caught up with Groovy? In this session, a Groovy project lead shows you how Groovy is still relevant in the JVM landscape, how similar or different Java and Groovy can be, how Groovy further improves the developer experience on top of JDK 8, and what Groovy offers beyond Java.</li>
<li><a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=5996">Groovy in 2014 and Beyond [CON5996]</a> With three million downloads in a year, Groovy still clearly leads the pack of alternative languages on the JVM, but it’s not resting on its laurels. The latest Groovy release, 2.3, is chock-full of useful new features and performance improvements. In particular, Groovy now supports the concept of “traits” for elegantly composing behaviors. Its JSON support is now the most performant of all the JSON libraries available to date. Groovy 2.3 introduces a new markup-based template engine, new code transformations, and much more. In this session, a Groovy project lead guides you through the latest advancements in the Groovy programming language and tells you what’s cooking for the next releases.</li>
<li><a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=1768">Exploring Groovy Metaprogramming [CON1768]</a> One of the key benefits of Groovy is its metaprogramming capability. The built-in features of the language enable you to realize AOP without the need for any heavyweight tools. This presentation takes an in-depth look at the metaprogramming capabilities of Groovy and explains when to use mixins, method injection, and method synthesis.</li>
<li><a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=3538">Functional Programmning the Groovy Way [CON3538]</a> In recent years, functional programming has gained ground over object-oriented programming, mainly due to the advancement in computing power. The JVM is no exception. You can find powerful contenders in Clojure and Scala, but Groovy is not that far back in the race. The Groovy programming language contains a wide array of APIs and features that facilitate a functional programming style, such as closure composition, memorization, trampolines, and iterator methods. This session explores all of these features that are sure to spice up your daily experience.</li>
<li><a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=2425">Rethinking API Design with Groovy Traits [CON2425]</a> Groovy 2.3 introduces the concept of traits in the language. Traits look like interfaces but enable the developer to add both implementation and state. They introduce multiple inheritance in the language while avoiding the diamond problem. Traits will let you rethink the way you design APIs in Groovy, by favoring composition of behaviors. This session explains what traits are, how they were implemented in Groovy, and what they have to offer to make your code more readable and maintainable.</li>
<li><a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=1764">Groovy and Grails Puzzlers: As Usual—Traps, Pitfalls, and End Cases [CON1764]</a> Remember the epic Java puzzlers? Here’s the Groovy version, and there are some neat ones! Even though it is totally a Grails shop, some of these had JFrog people scratching their head for days, trying to figure them out. And there is more! Contributions from the truly Groovy senseis, including @glaforge, @aalmiray, @tim_yates, and @kenkousen, make this session an unforgettable journey to Groovy’s O_O. You’ll get the expected dose of fun and enlightenment while hearing about mistakes and failures, great and small, in hard-core Groovy/Grails development.</li>
<li><a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=1769">Applying Groovy Closures for Fun and Productivity [CON1769]</a> You can program higher-order functions in Groovy quite easily by using closures, but the benefits of closures go far beyond that. Groovy has a variety of capabilities hidden in closures. This presentation unlocks that treasure and explores ways in which we can design applications by using Groovy closures to apply different design patterns, to create fluent interfaces, and even to program asynchrony.</li>
<li><a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=1703">HTML5/AngularJS/Groovy/Java and MongoDB Together: What Could Possibly Go Wrong? [CON1703]</a> We hear it’s common to create a minimum viable product (MVP) in a language that facilitates rapid prototyping and then migrate to the JVM when the application requires better stability and performance. In this session, the speaker uses Java to create a web application in less than hour. The JVM is a polyglot platform, and you’ll learn how to use the correct tools for this application, including AngularJS, Bootstrap, HTML5, web services, Java, MongoDB, and Groovy—it’s fully buzzword-compliant. The presentation doesn’t go into every technology in depth but demonstrates the role of each tool and how the tools interact. The session will result in a fully working mobile/browser-friendly app without compromise of design or good practice. It’s even going to have tests.</li>
<li><a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=1752">Plugging Users In: Extend Your Application with Pluggable Groovy DSL [CON1752]</a> It is often beneficial to enable users to extend your software with their own logic, and with dynamic languages on the JVM, it is also easy to do so. This session shares JFrog’s experience in creating a public, Groovy-authored user plug-in interface. It explains what domain-specific languages (DSLs) are, what their relevance to user plug-ins is, and how they can be implemented in Groovy or Java. It also discusses another very important aspect of pluggability: good public API design. Further, it covers security concerns and how they should be tackled. And finally, it discusses classpath isolation issues you may run into and compares different solutions to this problem.</li>
<li><a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=2939">Script Bowl 2014: The Battle Rages On [CON2939]</a> In this contest, languages that run on the JVM, represented by their respective language nerds, battle for bragging rights as the most popular language by showing off their shiny new features. Adding a twist to the format in which the audience members will pick this year’s winner(s): they will also vote on a language that should not return in 2015. Returning from 2013 are language gurus representing Clojure, Groovy, JRuby, and Scala. Attend this fun-filled and technically invigorating session to judge which scripting language best meets your needs. You’ll also be able to compare the respective languages and spark some thought-provoking discussions with the panelists that will be beneficial to the entire Java community.</li>
<li><a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=3693">Spring 4TW! [CON3693]</a> Spring 4’s here, and with it comes a bevy of new features designed to support modern web developers as well as enrich the component model and enable even-more-adaptive enterprise applications. Spring 4 also serves as the underpinning of Spring Boot, the entry point into the Spring platform. In this session, a Spring developer advocate discusses Spring 4’s new support for WebSocket (including extra support for STOMP), conditional configuration, dependency injection, Java 8 and Java EE 7 support (JCache support, the Batch JSR, ManagedExecutorService support, and so on), the Groovy language, and more. The presentation also looks at Spring Boot as a productive entry point into the Spring platform.</li>
<li><a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=7902">Writing Highly Concurrent Polyglot Applications with Vert.x [CON7902]</a> Vert.x is a lightweight, high-performance, polyglot, asynchronous application platform for the JVM. This session covers the design principles and motivation behind Vert.x, including its concurrency model, and discusses why Vert.x is a great fit for super-simple, highly concurrent applications. It also dives into some of the key Vert.x features, including the distributed event bus and high availability, and discusses some of the up-and-coming new features in the latest development branch for Vert.x 3.0, including distributed map support, management, and monitoring. The presentation includes demonstrations involving examples in several JVM languages such as Java, JavaScript, Scala, and Groovy.</li>
</ul>
<p>Update: I only searched sessions with the &ldquo;groovy&rdquo; keyword, but other Groovy ecosystem projects are also present, like Griffon, Gradle or Spock! So let me fix that by appending some more Groovy-related sessions to my list!</p>
<ul>
<li><a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=8780">Create Elegant Builds at Scale with Gradle [CON8780]</a> How can medium and large projects use Gradle to scale their build infrastructure? This session looks at organizations such as Netflix and LinkedIn and how they scale with Gradle across hundreds or thousands of developers. It discusses how to solve the challenges of standardization, dependency management, multilanguage builds, and automatic build infrastructure provisioning.</li>
<li><a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=3531">Gradle: Harder, Better, Stronger, Faster [CON3531]</a> Are you one of those people who search around the world looking for clues on how to get Maven to do your bidding, or do you just flip a coin and get lucky one more time? Maven works OK, but there’s a new tool in town that goes beyond Maven: Gradle. It’s fresh, almost aerodynamic. Once you get in contact with it, you’ll feel like doin’ it right when building a project. We kid you not, it’s an instant crush for those who have followed the Maven path, as you’ll find out in this session. This is the prime time of your life as a developer. Make the most out of it by leveraging the best tools.</li>
<li><a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=3553">Build, Test, and Deploy JavaFX Applications with Ease [CON3553]</a> Why is it that there are so many different options in the JVM space for building web applications but so few when it comes to desktop applications? This presentation addresses this question by showcasing a set of tools that can help you build JavaFX-powered applications without having to battle the build itself. It begins by looking at Gradle for defining the basic structure, then it covers Griffon as the means of putting all the pieces in their right place and TestFX as the testing library of choice, and finally it closes by circling back to Gradle and its deployment options.</li>
<li><a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=2276">Advanced Java Unit Testing with Spock [CON2276]</a> The Spock unit testing framework is on the verge of a 1.0 release and has already proven to be the next-generation thinking on how to test Java production code. Using Spock is hands-down the best way for developers to mock and test Java applications. One of Spock’s great features is the ability to write your own custom extensions. This session is a fast-paced (not for beginners), code-centric overview of how to use the advanced features in Spock, including custom extensions and interceptors.</li>
<li><a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=1579">BDD in Action: Building Software Right and Building the Right Software [CON1579]</a> A common perception of behavior-driven development (BDD) focuses on test automation with Cucumber-style “Given..When..Then” scenarios. But this is just the tip of the iceberg: in fact BDD ranges from requirements discovery and description through to driving technical design and implementation; helping testers focus their testing efforts more effectively; and even providing reliable, useful, and accurate technical documentation. This session discusses what BDD is about, its benefits, and how it affects development teams and processes. You will see how JVM teams can effectively implement BDD with tools such as JBehave, Cucumber, Thucydides, and Spock. Come learn how much more there is to BDD than just “Given..When..Then.”</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #26</title><link>https://glaforge.dev/posts/2014/06/17/groovy-weekly-26/</link><pubDate>Tue, 17 Jun 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/06/17/groovy-weekly-26/</guid><description>&lt;p>So what’s new this week?&lt;/p>
&lt;p>We can highlight the first beta of Groovy 2.4 with the Android support, so users can start having a go at writing Android applications in Groovy! As well as bug fixes releases for Grails 2.3.x and 2.4.x.&lt;/p>
&lt;p>Let’s also mention the launch of the Gradle plugin portal, announced last week at the Gradle Summit. And you’ll find also lots of presentations from the conference.&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>Groovy 2.3.3 released and &lt;a href="https://glaforge.dev/posts/2014/06/11/groovy-2-3-3-and-groovy-2-4-beta-1-with-android-support/">Groovy 2.4.0-beta-1 with Android support&lt;/a> is out too&lt;/li>
&lt;li>&lt;a href="https://twitter.com/grailsframework/status/478908515130830848">Grails 2.4.1 and 2.3.10&lt;/a> released with loads of bug fixes&lt;/li>
&lt;li>&lt;a href="https://spring.io/blog/2014/06/10/spring-boot-1-1-ga-released">Spring Boot 1.1&lt;/a> released&lt;/li>
&lt;li>&lt;a href="https://twitter.com/springboot/status/476821967018799104">Spring Boot 1.1.1&lt;/a> is out, fixing a little issue with the 1.1.0 release&lt;/li>
&lt;li>Marcin Erdmann announces the &lt;a href="http://grails.1312388.n4.nabble.com/ANN-Geb-0-9-3-released-td4657113.html">release of Geb 0.9.3&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/andreyhihlovski/status/476615698262007808">Gretty 0.0.24&lt;/a> is out, with full support of Spring Boot 1.1.0&lt;/li>
&lt;li>&lt;a href="https://twitter.com/grailsplugins/status/477022016994672642">Redis GORM 1.0.0&lt;/a> released&lt;/li>
&lt;li>Cédric Champeau released a &lt;a href="https://github.com/melix/jmh-gradle-plugin">JMH Gradle Plugin&lt;/a> that integrates the JMH micro-benchmarking framework with your Gradle build&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>Marc Palmer on the &lt;a href="http://transition.io/the-journey-from-objective-c-to-groovy/">journey from Objective-C to Groovy&lt;/a>&lt;/li>
&lt;li>MrHaki’s &lt;a href="http://gr8labs.org/getting-groovy/">Getting Groovy extensive tutorial&lt;/a>&lt;/li>
&lt;li>MrHaki&amp;rsquo;s Spock-light: on &lt;a href="http://mrhaki.blogspot.fr/2014/06/spocklight-extra-data-variables-for.html">using an extra data variable for unroll description&lt;/a>, based on Rob Fletcher&amp;rsquo;s GR8Conf Europe Spock talk&lt;/li>
&lt;li>Graeme Rocher blogs about a Gradle to &lt;a href="http://grails.io/post/88656787208/generating-pretty-groovy-2-3-groovydocs-with-gradle">get the nice GroovyDoc style&lt;/a> from Groovy 2.3+ even if your project is using Groovy 2.2 or below&lt;/li>
&lt;li>&lt;a href="http://thediscoblog.com/blog/2014/06/13/docker-containers-with-gradle-in-4-steps/">Docker containers with Gradle in 4 easy steps&lt;/a>, by Andy Glover&lt;/li>
&lt;li>Andrés Almiray wrote about the &lt;a href="http://www.jroller.com/aalmiray/entry/griffon_2_0_0_beta">Griffon 2.0.0 beta&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.objectpartners.com/2014/06/11/pooling-web-service-connections-in-grails/">Pooling web service connections in Grails&lt;/a> by Jeff Sheets&lt;/li>
&lt;li>Building enterprise Mule applications with Gradle: &lt;a href="http://blogs.mulesoft.org/building-mule-apps-gradle/">part 1&lt;/a>, &lt;a href="http://blogs.mulesoft.org/building-mule-apps-gradle-studio/">part 2&lt;/a>, &lt;a href="http://blogs.mulesoft.org/building-enterprise-apps-gradle/">part 3&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="presentations-from-the-gradle-summit">Presentations from the Gradle Summit&lt;/h2>
&lt;ul>
&lt;li>Justin Ryan from Netflix on the &lt;a href="http://fr.slideshare.net/quidryan/gradle-summit-2014-nebula">Nebula Gradle plugin, a useful test harness for Gradle plugins&lt;/a>&lt;/li>
&lt;li>David Carr on &lt;a href="http://www.slideshare.net/davidmc24/cloud-browser-testing-with-gradle-and-geb">cloud browser testing with Gradle and Geb&lt;/a>&lt;/li>
&lt;li>Benjamin Muschko on &lt;a href="https://speakerdeck.com/bmuschko/web-application-deployments-with-gradle-from-the-developers-machine-to-continuous-deployment">web application deployments with Gradle&lt;/a>&lt;/li>
&lt;li>Benjamin Muschko on &lt;a href="https://speakerdeck.com/bmuschko/provisioning-virtualized-infrastructure-with-gradle">provisioning virtualized infrastructure with Gradle&lt;/a>&lt;/li>
&lt;li>Tobias Gesellchen shares his slides and code snippets on &lt;a href="http://gesellix.github.io/gradle-summit-2014/">moving from Maven to Gradle&lt;/a>&lt;/li>
&lt;li>Rene Gröschke on &lt;a href="http://www.slideshare.net/breskeby/releasing-with-gradle-gradle-exchange-2014">releasing with Gradle&lt;/a>&lt;/li>
&lt;li>Dan Woods spoke about his &lt;a href="https://twitter.com/varzof/status/477519220150767616">Gradle provisioning plugin&lt;/a>&lt;/li>
&lt;li>Andrés Almiray was &lt;a href="http://grails.org.mx/2014/06/11/resumen-24ta-reunion/">spreading the word about Groovy, Gradle, Spock&lt;/a> and more in Mexico (Spanish content)&lt;/li>
&lt;/ul>
&lt;h2 id="news">News&lt;/h2>
&lt;ul>
&lt;li>Gradleware just announced the &lt;a href="http://plugins.gradle.org/">launch of the Gradle plugin portal&lt;/a>&lt;/li>
&lt;li>Guillaume Laforge shares the &lt;a href="http://beta.groovy-lang.org/docs/groovy-2.3.4-SNAPSHOT/html/documentation/">future look of the new Groovy documentation&lt;/a>&lt;/li>
&lt;li>Jacobe Aae Mikkelsen on the &lt;a href="http://grydeske.net/news/show/49">Grails Diary week 24&lt;/a>&lt;/li>
&lt;li>Learn about the &lt;a href="http://grooscript.org/roadmap.html">GrooScript roadmap&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="mailing-list-posts">Mailing-list posts&lt;/h2>
&lt;ul>
&lt;li>An interesting thread on &lt;a href="http://groovy.329449.n5.nabble.com/Need-help-with-quot-Groovy-sales-pitch-quot-td5720060.html">selling Groovy to your management&lt;/a>&lt;/li>
&lt;li>A &lt;a href="http://gradle.1045684.n5.nabble.com/Regression-list-for-1-12-td5712639.html#a5712650">“provided scope”&lt;/a> might come to Gradle&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>An &lt;a href="http://blog.nvisium.com/2014/06/introducing-grailsnv-vulnerable-groovy.html">intentionally vulnerable Grails application&lt;/a> for education purpose&lt;/li>
&lt;li>A demo project show-casing the &lt;a href="https://github.com/radimk/gradle-toolingApi-demo">Gradle tooling API&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="contributions">Contributions&lt;/h2>
&lt;ul>
&lt;li>Grant McConnaughey contributed &lt;a href="https://twitter.com/glaforge/status/478085555407040512">documentation on Groovy “statements”&lt;/a>. Thank you, Grant. To all, please don’t hesitate to help us and contribute to the documentation effort!&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>A good way to get started with Groovy, with the &lt;a href="https://twitter.com/geeky_android/status/478164163349721088">GroovyKoans&lt;/a>&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/jfarcand/status/477453724299042816">Estonian emergency system is powered by Grails&lt;/a> and Atmosphere&lt;/li>
&lt;li>&lt;a href="https://twitter.com/danveloper/status/477113115515953153">Gradle 2.0 is 20% to 70% more performant&lt;/a> than Gradle 1.0&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/ratpackweb/status/477128712622579713">Gradle plugin portal is built with Ratpack&lt;/a> and RxJava&lt;/li>
&lt;li>You can start &lt;a href="https://twitter.com/danveloper/status/477119125966188546">submitting your Gradle plugin to the Gradle plugin portal&lt;/a>&lt;/li>
&lt;li>Cédric Champeau announces his &lt;a href="https://twitter.com/cedricchampeau/status/478514436358275072">JMH Gradle plugin&lt;/a> and showing a sample project using it&lt;/li>
&lt;li>Andrés Almiray things the &lt;a href="https://twitter.com/aalmiray/status/477734872946319361">Gradle plugin portal has been rushed a bit&lt;/a>, in time for Gradle Summit, as it&amp;rsquo;s still a bit complicated to submit plugins for inclusion&lt;/li>
&lt;li>Cédric Champeau notices only minor changes are needed to be able to &lt;a href="https://twitter.com/cedricchampeau/status/476638077323341824">build Groovy with Gradle 2.0-rc-1&lt;/a>&lt;/li>
&lt;li>Initial work from Russell Hart bringing &lt;a href="https://twitter.com/ratpackweb/status/476210105319309314">Ratpack into the &amp;ldquo;Reactive Streams&amp;rdquo;&lt;/a> family&lt;/li>
&lt;li>Cédric Champeau would like to see the &lt;a href="https://twitter.com/cedricchampeau/status/476752756376891392">&amp;ldquo;new&amp;rdquo; keyword totally optional&lt;/a> in Groovy, without using @Newify&lt;/li>
&lt;li>Guillaume Laforge suggests creating an &lt;a href="https://twitter.com/glaforge/status/477359185253134336">&amp;ldquo;Enterprise&amp;rdquo; version of Groovy&amp;rsquo;s &amp;ldquo;spaceshift&amp;rdquo; operator&lt;/a>&lt;/li>
&lt;li>Magnus Rundberget &lt;a href="https://twitter.com/mrundberget/status/476645367808008192">explores the Ratpack Gradle dependencies&lt;/a> with the LightTable Groovy plugin 0.0.6&lt;/li>
&lt;li>The Grails &lt;a href="https://twitter.com/grailsframework/status/477124659574738945">GORM groovydoc&lt;/a> is available standalone&lt;/li>
&lt;li>Cédric Champeau is surprised when people say using &lt;a href="https://twitter.com/cedricchampeau/status/477079037098340353">“def” makes a dynamic type (but “def” == “Object”)&lt;/a>&lt;/li>
&lt;li>Cédric Champeau remarks that &lt;a href="https://twitter.com/cedricchampeau/status/477079421569212416">Groovy is a strongly typed language&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/477425205716217856">CRaSH 1.3.0&lt;/a> is available in GVM&lt;/li>
&lt;li>CrudZilla’s &lt;a href="https://twitter.com/crudzillasoft/status/477560344542334977">HiveMind web app generator&lt;/a> is written largely in Groovy through JSR-223&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/dailygrailstip/status/477860347601092610">Gradle Node plugin&lt;/a> gives you an easy way to install and run Node.JS on Continuous Integration environments&lt;/li>
&lt;li>Burt Beckwith is going to each &lt;a href="https://twitter.com/burtbeckwith/status/477253832120881152">Groovy and Grails again at Harvard&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="jobs">Jobs&lt;/h2>
&lt;ul>
&lt;li>A &lt;a href="https://twitter.com/findgrailsjobs/status/476758016680013824">Groovy / Grails developer for Plano in Texas&lt;/a>, USA&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau will speak about &lt;a href="https://twitter.com/CedricChampeau/status/477010580260528130">Groovy Traits at JavaOne&lt;/a>&lt;/li>
&lt;li>JavaOne session acceptance emails are starting to flow, and the first Groovy sessions are appearing, for instance Guillaume Laforge&amp;rsquo;s on the &lt;a href="https://twitter.com/glaforge/status/476657140128817152">latest &amp;amp; greatest Groovy, or Groovy in the light of Java 8&lt;/a>&lt;/li>
&lt;li>MrHaki&amp;rsquo;s &lt;a href="http://blog.jdriven.com/2014/06/gr8conf-2014-europe-conference-report/">report on GR8Conf&lt;/a> Europe 2014&lt;/li>
&lt;li>Quick summary from Andrés Almiray about the &lt;a href="http://www.jroller.com/aalmiray/entry/hackergarten_gr8conf">GR8Conf Europe 2014 Hackergarten&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>So what’s new this week?</p>
<p>We can highlight the first beta of Groovy 2.4 with the Android support, so users can start having a go at writing Android applications in Groovy! As well as bug fixes releases for Grails 2.3.x and 2.4.x.</p>
<p>Let’s also mention the launch of the Gradle plugin portal, announced last week at the Gradle Summit. And you’ll find also lots of presentations from the conference.</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Groovy 2.3.3 released and <a href="https://glaforge.dev/posts/2014/06/11/groovy-2-3-3-and-groovy-2-4-beta-1-with-android-support/">Groovy 2.4.0-beta-1 with Android support</a> is out too</li>
<li><a href="https://twitter.com/grailsframework/status/478908515130830848">Grails 2.4.1 and 2.3.10</a> released with loads of bug fixes</li>
<li><a href="https://spring.io/blog/2014/06/10/spring-boot-1-1-ga-released">Spring Boot 1.1</a> released</li>
<li><a href="https://twitter.com/springboot/status/476821967018799104">Spring Boot 1.1.1</a> is out, fixing a little issue with the 1.1.0 release</li>
<li>Marcin Erdmann announces the <a href="http://grails.1312388.n4.nabble.com/ANN-Geb-0-9-3-released-td4657113.html">release of Geb 0.9.3</a></li>
<li><a href="https://twitter.com/andreyhihlovski/status/476615698262007808">Gretty 0.0.24</a> is out, with full support of Spring Boot 1.1.0</li>
<li><a href="https://twitter.com/grailsplugins/status/477022016994672642">Redis GORM 1.0.0</a> released</li>
<li>Cédric Champeau released a <a href="https://github.com/melix/jmh-gradle-plugin">JMH Gradle Plugin</a> that integrates the JMH micro-benchmarking framework with your Gradle build</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Marc Palmer on the <a href="http://transition.io/the-journey-from-objective-c-to-groovy/">journey from Objective-C to Groovy</a></li>
<li>MrHaki’s <a href="http://gr8labs.org/getting-groovy/">Getting Groovy extensive tutorial</a></li>
<li>MrHaki&rsquo;s Spock-light: on <a href="http://mrhaki.blogspot.fr/2014/06/spocklight-extra-data-variables-for.html">using an extra data variable for unroll description</a>, based on Rob Fletcher&rsquo;s GR8Conf Europe Spock talk</li>
<li>Graeme Rocher blogs about a Gradle to <a href="http://grails.io/post/88656787208/generating-pretty-groovy-2-3-groovydocs-with-gradle">get the nice GroovyDoc style</a> from Groovy 2.3+ even if your project is using Groovy 2.2 or below</li>
<li><a href="http://thediscoblog.com/blog/2014/06/13/docker-containers-with-gradle-in-4-steps/">Docker containers with Gradle in 4 easy steps</a>, by Andy Glover</li>
<li>Andrés Almiray wrote about the <a href="http://www.jroller.com/aalmiray/entry/griffon_2_0_0_beta">Griffon 2.0.0 beta</a></li>
<li><a href="http://www.objectpartners.com/2014/06/11/pooling-web-service-connections-in-grails/">Pooling web service connections in Grails</a> by Jeff Sheets</li>
<li>Building enterprise Mule applications with Gradle: <a href="http://blogs.mulesoft.org/building-mule-apps-gradle/">part 1</a>, <a href="http://blogs.mulesoft.org/building-mule-apps-gradle-studio/">part 2</a>, <a href="http://blogs.mulesoft.org/building-enterprise-apps-gradle/">part 3</a></li>
</ul>
<h2 id="presentations-from-the-gradle-summit">Presentations from the Gradle Summit</h2>
<ul>
<li>Justin Ryan from Netflix on the <a href="http://fr.slideshare.net/quidryan/gradle-summit-2014-nebula">Nebula Gradle plugin, a useful test harness for Gradle plugins</a></li>
<li>David Carr on <a href="http://www.slideshare.net/davidmc24/cloud-browser-testing-with-gradle-and-geb">cloud browser testing with Gradle and Geb</a></li>
<li>Benjamin Muschko on <a href="https://speakerdeck.com/bmuschko/web-application-deployments-with-gradle-from-the-developers-machine-to-continuous-deployment">web application deployments with Gradle</a></li>
<li>Benjamin Muschko on <a href="https://speakerdeck.com/bmuschko/provisioning-virtualized-infrastructure-with-gradle">provisioning virtualized infrastructure with Gradle</a></li>
<li>Tobias Gesellchen shares his slides and code snippets on <a href="http://gesellix.github.io/gradle-summit-2014/">moving from Maven to Gradle</a></li>
<li>Rene Gröschke on <a href="http://www.slideshare.net/breskeby/releasing-with-gradle-gradle-exchange-2014">releasing with Gradle</a></li>
<li>Dan Woods spoke about his <a href="https://twitter.com/varzof/status/477519220150767616">Gradle provisioning plugin</a></li>
<li>Andrés Almiray was <a href="http://grails.org.mx/2014/06/11/resumen-24ta-reunion/">spreading the word about Groovy, Gradle, Spock</a> and more in Mexico (Spanish content)</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Gradleware just announced the <a href="http://plugins.gradle.org/">launch of the Gradle plugin portal</a></li>
<li>Guillaume Laforge shares the <a href="http://beta.groovy-lang.org/docs/groovy-2.3.4-SNAPSHOT/html/documentation/">future look of the new Groovy documentation</a></li>
<li>Jacobe Aae Mikkelsen on the <a href="http://grydeske.net/news/show/49">Grails Diary week 24</a></li>
<li>Learn about the <a href="http://grooscript.org/roadmap.html">GrooScript roadmap</a></li>
</ul>
<h2 id="mailing-list-posts">Mailing-list posts</h2>
<ul>
<li>An interesting thread on <a href="http://groovy.329449.n5.nabble.com/Need-help-with-quot-Groovy-sales-pitch-quot-td5720060.html">selling Groovy to your management</a></li>
<li>A <a href="http://gradle.1045684.n5.nabble.com/Regression-list-for-1-12-td5712639.html#a5712650">“provided scope”</a> might come to Gradle</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>An <a href="http://blog.nvisium.com/2014/06/introducing-grailsnv-vulnerable-groovy.html">intentionally vulnerable Grails application</a> for education purpose</li>
<li>A demo project show-casing the <a href="https://github.com/radimk/gradle-toolingApi-demo">Gradle tooling API</a></li>
</ul>
<h2 id="contributions">Contributions</h2>
<ul>
<li>Grant McConnaughey contributed <a href="https://twitter.com/glaforge/status/478085555407040512">documentation on Groovy “statements”</a>. Thank you, Grant. To all, please don’t hesitate to help us and contribute to the documentation effort!</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>A good way to get started with Groovy, with the <a href="https://twitter.com/geeky_android/status/478164163349721088">GroovyKoans</a></li>
<li>The <a href="https://twitter.com/jfarcand/status/477453724299042816">Estonian emergency system is powered by Grails</a> and Atmosphere</li>
<li><a href="https://twitter.com/danveloper/status/477113115515953153">Gradle 2.0 is 20% to 70% more performant</a> than Gradle 1.0</li>
<li>The <a href="https://twitter.com/ratpackweb/status/477128712622579713">Gradle plugin portal is built with Ratpack</a> and RxJava</li>
<li>You can start <a href="https://twitter.com/danveloper/status/477119125966188546">submitting your Gradle plugin to the Gradle plugin portal</a></li>
<li>Cédric Champeau announces his <a href="https://twitter.com/cedricchampeau/status/478514436358275072">JMH Gradle plugin</a> and showing a sample project using it</li>
<li>Andrés Almiray things the <a href="https://twitter.com/aalmiray/status/477734872946319361">Gradle plugin portal has been rushed a bit</a>, in time for Gradle Summit, as it&rsquo;s still a bit complicated to submit plugins for inclusion</li>
<li>Cédric Champeau notices only minor changes are needed to be able to <a href="https://twitter.com/cedricchampeau/status/476638077323341824">build Groovy with Gradle 2.0-rc-1</a></li>
<li>Initial work from Russell Hart bringing <a href="https://twitter.com/ratpackweb/status/476210105319309314">Ratpack into the &ldquo;Reactive Streams&rdquo;</a> family</li>
<li>Cédric Champeau would like to see the <a href="https://twitter.com/cedricchampeau/status/476752756376891392">&ldquo;new&rdquo; keyword totally optional</a> in Groovy, without using @Newify</li>
<li>Guillaume Laforge suggests creating an <a href="https://twitter.com/glaforge/status/477359185253134336">&ldquo;Enterprise&rdquo; version of Groovy&rsquo;s &ldquo;spaceshift&rdquo; operator</a></li>
<li>Magnus Rundberget <a href="https://twitter.com/mrundberget/status/476645367808008192">explores the Ratpack Gradle dependencies</a> with the LightTable Groovy plugin 0.0.6</li>
<li>The Grails <a href="https://twitter.com/grailsframework/status/477124659574738945">GORM groovydoc</a> is available standalone</li>
<li>Cédric Champeau is surprised when people say using <a href="https://twitter.com/cedricchampeau/status/477079037098340353">“def” makes a dynamic type (but “def” == “Object”)</a></li>
<li>Cédric Champeau remarks that <a href="https://twitter.com/cedricchampeau/status/477079421569212416">Groovy is a strongly typed language</a></li>
<li><a href="https://twitter.com/gvmtool/status/477425205716217856">CRaSH 1.3.0</a> is available in GVM</li>
<li>CrudZilla’s <a href="https://twitter.com/crudzillasoft/status/477560344542334977">HiveMind web app generator</a> is written largely in Groovy through JSR-223</li>
<li>The <a href="https://twitter.com/dailygrailstip/status/477860347601092610">Gradle Node plugin</a> gives you an easy way to install and run Node.JS on Continuous Integration environments</li>
<li>Burt Beckwith is going to each <a href="https://twitter.com/burtbeckwith/status/477253832120881152">Groovy and Grails again at Harvard</a></li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li>A <a href="https://twitter.com/findgrailsjobs/status/476758016680013824">Groovy / Grails developer for Plano in Texas</a>, USA</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>Cédric Champeau will speak about <a href="https://twitter.com/CedricChampeau/status/477010580260528130">Groovy Traits at JavaOne</a></li>
<li>JavaOne session acceptance emails are starting to flow, and the first Groovy sessions are appearing, for instance Guillaume Laforge&rsquo;s on the <a href="https://twitter.com/glaforge/status/476657140128817152">latest &amp; greatest Groovy, or Groovy in the light of Java 8</a></li>
<li>MrHaki&rsquo;s <a href="http://blog.jdriven.com/2014/06/gr8conf-2014-europe-conference-report/">report on GR8Conf</a> Europe 2014</li>
<li>Quick summary from Andrés Almiray about the <a href="http://www.jroller.com/aalmiray/entry/hackergarten_gr8conf">GR8Conf Europe 2014 Hackergarten</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.3.3 and Groovy 2.4-beta-1 with Android support</title><link>https://glaforge.dev/posts/2014/06/11/groovy-2-3-3-and-groovy-2-4-beta-1-with-android-support/</link><pubDate>Wed, 11 Jun 2014 00:10:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/06/11/groovy-2-3-3-and-groovy-2-4-beta-1-with-android-support/</guid><description>&lt;p>The Groovy team is very happy to announce the joint &lt;strong>releases of Groovy 2.3.3&lt;/strong> and &lt;strong>Groovy 2.4-beta-1&lt;/strong>.&lt;/p>
&lt;p>&lt;strong>Groovy 2.3.3 is a bug fix release&lt;/strong>, particularly covering the recent issues discovered around anonymous inner classes. Please upgrade to 2.3.3 if you&amp;rsquo;ve been facing such issues.&lt;/p>
&lt;p>Groovy 2.4.0-beta-1 comes pretty early, as we would like to come back to releasing milestones on a more regular pace, compared with how late we released the first beta of Groovy 2.3. We really want you, our users, to be able to give us feedback as soon as possible!&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy team is very happy to announce the joint <strong>releases of Groovy 2.3.3</strong> and <strong>Groovy 2.4-beta-1</strong>.</p>
<p><strong>Groovy 2.3.3 is a bug fix release</strong>, particularly covering the recent issues discovered around anonymous inner classes. Please upgrade to 2.3.3 if you&rsquo;ve been facing such issues.</p>
<p>Groovy 2.4.0-beta-1 comes pretty early, as we would like to come back to releasing milestones on a more regular pace, compared with how late we released the first beta of Groovy 2.3. We really want you, our users, to be able to give us feedback as soon as possible!</p>
<p>The key aspect of <strong>Groovy 2.4.0-beta-1 is that it&rsquo;s the first version of Groovy that officially provides support for running Groovy on Android</strong>.</p>
<p>You can learn more about the Groovy support for Android in those two articles from Cédric&rsquo;s blog, and the Groovy Android Gradle plugin:</p>
<ul>
<li><a href="http://melix.github.io/blog/2014/06/grooid.html">Groovy on Android</a></li>
<li><a href="http://melix.github.io/blog/2014/06/grooid2.html">The technical details about Groovy on Android</a></li>
<li><a href="https://github.com/melix/groovy-android-gradle-plugin">Groovy Android Gradle plugin</a></li>
</ul>
<p>You can have a look at the JIRA release notes:</p>
<ul>
<li><a href="https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=20415">Groovy 2.3.3 JIRA release notes</a></li>
<li><a href="https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=20369">Groovy 2.4.0-beta-1 JIRA release notes</a></li>
</ul>
<p>You can download both versions from our <a href="http://groovy.codehaus.org/Download">download area</a>.</p>
<p>Have a look at the <a href="http://beta.groovy-lang.org/docs/latest/html/documentation/">latest documentation for Groovy 2.3.3</a>, and don&rsquo;t hesitate to contribute to it, your help will be more than welcome.</p>
<p>Groovy developers, Grails developers, and other members of the Groovy ecosystem met in Copenhagen last week for a <strong>Groovy DevCon meetup</strong> (kindly sponsored by <a href="http://gr8conf.eu/">GR8Conf Europe</a>) to speak about the ongoing development of Groovy. You might want to have a look at the <a href="http://bit.ly/gdc10notes">notes of the meeting</a>, to learn more about our discussions and what we&rsquo;ll be working on for Groovy.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #25</title><link>https://glaforge.dev/posts/2014/06/11/groovy-weekly-25/</link><pubDate>Wed, 11 Jun 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/06/11/groovy-weekly-25/</guid><description>&lt;p>&lt;a href="http://gr8conf.eu/">GR8Conf Europe&lt;/a> is over, but there’s a lot to learn from the conference, with all the interesting conference sessions that took place there in Copenhagen.&lt;/p>
&lt;p>The themes for this week will be around GR8Conf, of course, but also about Groovy on Android, again the Swift language, and also the first impressive demo of Grails 3.0!&lt;/p>
&lt;p>Right after the conference also took place the annual Groovy DevCon meetup, gathering core members of the Groovy team, Grails team, and members of the Groovy ecosystem. Lots of interesting tidbits about &lt;a href="http://bit.ly/gdc10notes">what’s coming up in Groovy&lt;/a>!&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://gr8conf.eu/">GR8Conf Europe</a> is over, but there’s a lot to learn from the conference, with all the interesting conference sessions that took place there in Copenhagen.</p>
<p>The themes for this week will be around GR8Conf, of course, but also about Groovy on Android, again the Swift language, and also the first impressive demo of Grails 3.0!</p>
<p>Right after the conference also took place the annual Groovy DevCon meetup, gathering core members of the Groovy team, Grails team, and members of the Groovy ecosystem. Lots of interesting tidbits about <a href="http://bit.ly/gdc10notes">what’s coming up in Groovy</a>!</p>
<p>Fasten your seat belt, and without further ado, let’s get started!</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Andrés Almiray announces the <a href="http://griffon.markmail.org/thread/cispnhhnwqo4z5n3">release of Griffon 2.0.0.BETA</a></li>
<li>Cédric Champeau released <a href="https://twitter.com/CedricChampeau/status/475570875157458944">version 0.2 of the Groovy Android plugin fpr Gradle</a> to simplify developments of Android apps in Groovy</li>
<li><a href="http://forums.gradle.org/gradle/topics/gradle_2_0_rc_1_is_now_available_for_testing">Gradle 2.0-RC-1</a> is available for testing</li>
<li>Bertrand Goetzmann <a href="https://twitter.com/bgoetzmann/status/473929435469864961">updated Afterburner.gfx</a> to the latest GroovyFX 0.4.0 release</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Guillaume Laforge shared the <a href="http://bit.ly/gdc10notes">notes of the Groovy DevCon #10 meetup</a> that took place in Copenhagen right after GR8Conf Europe 2014</li>
<li>See how developing mobile <a href="http://melix.github.io/blog/2014/06/grooid.html">Android apps in Groovy can be a streamlined</a> in Cédric Champeau’s article</li>
<li>Cédric Champeau gives more <a href="http://melix.github.io/blog/2014/06/grooid2.html">technical details about the Groovy on Android</a></li>
<li>JAXEnter believes <a href="http://jaxenter.com/groovy-is-the-swift-alternative-for-android.1-50449.html">Groovy is the Swift-alternative for Android</a></li>
<li>Luke Daley gives a <a href="http://ldaley.com/post/87844640597/ratpack-update">detailed update about the status of the Ratpack</a> project</li>
<li><a href="http://solidsoft.wordpress.com/2014/06/05/null-object-pattern-implementation-in-groovy-a-map-with-default-behavior/">Null Object pattern</a> implementation in Groovy by Marcin Zajączkowski</li>
<li>Adding <a href="http://groovy.dzone.com/articles/grails-adding-javascript">JavaScript at the bottom of a Grails page</a> by Venkatt Guhesan</li>
</ul>
<h2 id="presentations--gr8conf-europe-special-edition">Presentations — GR8Conf Europe special edition</h2>
<ul>
<li><a href="https://twitter.com/gr8conf/status/474164749999935489">video</a> and <a href="https://speakerdeck.com/glaforge/groovy-in-2014-and-beyond-at-gr8conf-europe-2014">slides</a> of Guillaume Laforge’s Groovy update talk</li>
<li>Cédric Champeau’s presentation and code of the <a href="https://twitter.com/cedricchampeau/status/474152898361049088">first Android app in Groovy</a></li>
<li><a href="https://twitter.com/rfletcherEW/status/474262034641866753">Idiomatic Spock</a> talk by Rob Fletcher</li>
<li>Marco Vermeulen’s slides and code about his presentation on <a href="https://twitter.com/marcovermeulen/status/474155245271269376">Spring Boot microservices</a></li>
<li>Slides and source for Marco Vermeulen’s <a href="https://twitter.com/marcovermeulen/status/474158629705486337">Behavior Driven Development talk with Groovy and Cucumber</a></li>
<li>Julien Viet presented <a href="http://fr.slideshare.net/gr8conf/bd-2036-crashslide2">Crash, the shell for the JVM</a></li>
<li>slides and code from Göran Ehrsson on <a href="https://twitter.com/gr8conf/status/474164749999935489">Grails feature plugins</a></li>
<li>MrHaki’s Gradle Goodness <a href="https://twitter.com/mrhaki/status/474112588876640256">slides and code</a></li>
<li>Alexander Klein on <a href="https://twitter.com/saschaklein/status/474157270671306752">Vert.x with Groovy</a></li>
<li>Lari Hotari speaks about <a href="https://twitter.com/lhotari/status/474459744069902337">Grails 3, Ratpack and Spring Boot</a></li>
<li>Claus Ibsen on Apache <a href="http://java.dzone.com/articles/apache-camel-groovy-and-hawtio">Camel, Hawtio and Groovy</a></li>
<li>Benoît Hédiard presented a talk titled &ldquo;<a href="https://medium.com/@benorama/running-a-startup-with-lean-devops-culture-df50825b110b">Running a Startup with Lean &amp; DevOps culture</a>&rdquo; where his weapons of choices were Groovy and Grails</li>
</ul>
<h2 id="interview">Interview</h2>
<ul>
<li><a href="https://twitter.com/gr8conf/status/474164749999935489">Cédric Champeau’s</a> <a href="http://www.infoq.com/news/2014/06/groovy-android">interview by InfoQ about Groovy running on Android</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Jacob Aae Mikkelsen <a href="http://grydeske.net/news/show/48">week 22 and 23 of the Grails Diary</a></li>
<li>EdX is featuring a <a href="https://twitter.com/headinthebox/status/474808090802802688">Functional Programming class</a> by Erik Meijer that might get a little Groovy love</li>
</ul>
<h2 id="google-posts">Google+ posts</h2>
<ul>
<li>Mark Perry created a new module for FunctionalGroovy for <a href="https://plus.google.com/b/101432359761228268146/103753917802203497881/posts/2TnU2cdFqX8?cfem=1">improving the usage of Java 8&rsquo;s Optional</a> class</li>
<li>Magnus Rundberget reports nice contributions from GR8Conf Europe 2014&rsquo;s hackergarten <a href="https://github.com/rundis/LightTable-Groovy/releases/tag/0.0.6">improving the Groovy support in Light Table</a>, particularly around the Gradle dependency graph</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>A <a href="https://github.com/gregopet/flappy-gru">Flappy Bird clone for Android in Groovy</a>!</li>
<li>The Groovy Remote Control plugin is an <a href="https://twitter.com/dailygrailstip/status/474060855944818688">example of a Jenkins plugin written in Groovy</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>At GR8Conf Europe, Graeme Rocher showed a <a href="https://gist.github.com/glaforge/c44f439974b631a384e9">super lean example of a RESTful micro-service in Grails 3.0</a> with just a single class!</li>
<li>Cédric Champeau gives all the links regarding <a href="https://twitter.com/cedricchampeau/status/474314754987941888">Groovy on Android</a>, with his presentation slides from GR8Conf, the code of the GR8Conf agenda application</li>
<li>Looks like <a href="https://twitter.com/nobeans/status/474431420224393216">Groovy on Android is a hot topic in Japan</a>!</li>
<li>Graeme Rocher’s <a href="https://twitter.com/puneetbhl/status/474461108989075456">Grails 3 goals</a> slide from his GR8Conf Europe presentation</li>
<li>Lari Hotari demonstrated a <a href="https://twitter.com/ilopmar/status/474099772664852480">Spring Boot application, inside Ratpack, using Grails GORM</a>, at GR8Conf Europe</li>
<li>During his presentation at GR8Conf Europe, MrHaki was <a href="https://twitter.com/mrhaki/status/474113095733100545">using the Gradle_Language package for SublimeText</a></li>
<li>Dan Vega is <a href="https://twitter.com/cfaddict/status/474036220092497920">enhancing Apple’s Swift language with Groovy methods</a></li>
<li>Andrés Almiray points at the fact you can <a href="https://twitter.com/aalmiray/status/474119215461314560">try Crash online</a> with the examples shown by Julien Viet during GR8Conf Europe</li>
<li>Pictures of the GR8Conf Europe 2014 event:
<ul>
<li><a href="https://twitter.com/gr8conf/status/474130035084115969">first day</a></li>
<li><a href="https://twitter.com/gr8conf/status/474130692012802048">second day</a></li>
<li><a href="http://bit.ly/gr8conf-2014-day-3">third day</a></li>
</ul>
</li>
<li>Iván López notes that the <a href="https://twitter.com/ilopmar/status/474129638818856960">first Android application written in Groovy is the GR8Conf agenda application</a> built by Cédric Champeau</li>
<li>Dan Woods enjoy the <a href="https://twitter.com/danveloper/status/474681276252028929">method-level granularity</a> of what you decides should be dynamic or statically compiled in Groovy</li>
<li><a href="https://twitter.com/gvmtool/status/474856227965779968">Gradle 2.0-rc-1</a> available on GVM</li>
<li>Ben Edwards reminds us that the <a href="https://twitter.com/alttext/status/474999654762160128">SmartThings IoT platform is using Groovy</a> as its intelligence language</li>
<li>It&rsquo;s easy to <a href="https://twitter.com/theaviary/status/475624761222246400">create Griffon applications with GVM, Gradle, and Lazybones</a></li>
<li><a href="https://twitter.com/gvmtool/status/475751295396380672">GVM adds Crash</a> to the list of candidates</li>
<li><a href="https://twitter.com/gvmtool/status/476612688001662976">Spring Boot 1.1</a> is available on GVM</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li><a href="https://twitter.com/jbaruch/status/476283524337520640">Gradle in Action is 50% off today</a></li>
<li>Greg Turnquist recently wrapped up chapter 1 (&ldquo;getting started with Groovy&rdquo;) of his upcoming <a href="http://blog.greglturnquist.com/2014/06/recently-wrapped-up-chapter-1-quick-start-with-groovy.html">Spring Boot book</a></li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li>Searching for a <a href="https://twitter.com/findgrailsjobs/status/474561040081235969">Grails developer in Berlin</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>The GR8Ladies are partnering with TCGeekettes for a <a href="https://twitter.com/jennapederson/status/474577644336324608">GR8 workshop on June 28th</a></li>
<li><a href="https://twitter.com/springcentral/status/474646591073435648">More talks selected for SpringOne2GX</a> in Dallas</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Latest Groovy 2.3 coverage presented at GR8Conf Europe</title><link>https://glaforge.dev/talks/2014/06/04/latest-groovy-2-3-coverage-presented-at-gr8conf-europe/</link><pubDate>Wed, 04 Jun 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/talks/2014/06/04/latest-groovy-2-3-coverage-presented-at-gr8conf-europe/</guid><description>&lt;p>I&amp;rsquo;ve published the slides of my &amp;ldquo;Groovy in 2014 and beyond&amp;rdquo; presentation at GR8Conf Europe.&lt;/p>
&lt;p>Slides embedded below:&lt;/p>
&lt;script async class="speakerdeck-embed" data-id="b40bc2e0cdef01316bf51e853270e897" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js">&lt;/script>
&lt;p>As well as video:&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/P63OHYc3q8s?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video">&lt;/iframe>
&lt;/div></description><content:encoded>
<![CDATA[<p>I&rsquo;ve published the slides of my &ldquo;Groovy in 2014 and beyond&rdquo; presentation at GR8Conf Europe.</p>
<p>Slides embedded below:</p>
<script async class="speakerdeck-embed" data-id="b40bc2e0cdef01316bf51e853270e897" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>As well as video:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/P63OHYc3q8s?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #24</title><link>https://glaforge.dev/posts/2014/06/03/groovy-weekly-24/</link><pubDate>Tue, 03 Jun 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/06/03/groovy-weekly-24/</guid><description>&lt;p>Direct live from &lt;a href="http://gr8conf.eu/">GR8Conf Europe&lt;/a>!&lt;/p>
&lt;p>Several speakers already uploaded their presentation slides, as you shall see in the “presentations” sections. And the videos will be available later after the conference.&lt;/p>
&lt;p>At the same time in San Francisco, Apple is running its WWDC conference and announced the creation of a new language, &lt;a href="https://developer.apple.com/swift/">Swift&lt;/a>, that &lt;a href="https://glaforge.dev/posts/2014/06/02/apple-s-swift-programming-language-inspired-by-groovy/">looks a lot like Groovy&lt;/a> as several Groovy users have already noticed on Twitter. That’s nice to see Groovy also influences other languages!&lt;/p></description><content:encoded>
<![CDATA[<p>Direct live from <a href="http://gr8conf.eu/">GR8Conf Europe</a>!</p>
<p>Several speakers already uploaded their presentation slides, as you shall see in the “presentations” sections. And the videos will be available later after the conference.</p>
<p>At the same time in San Francisco, Apple is running its WWDC conference and announced the creation of a new language, <a href="https://developer.apple.com/swift/">Swift</a>, that <a href="https://glaforge.dev/posts/2014/06/02/apple-s-swift-programming-language-inspired-by-groovy/">looks a lot like Groovy</a> as several Groovy users have already noticed on Twitter. That’s nice to see Groovy also influences other languages!</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Guillaume Laforge announces the <a href="https://glaforge.dev/posts/2014/05/28/groovy-2-3-2-released/">Groovy 2.3.2 bug-fix</a> release</li>
<li>CRaSH, the JVM shell, updated its <a href="https://twitter.com/crashub/status/471630363413217281">Grails integration</a></li>
<li><a href="https://twitter.com/grooscript/status/471791908839489536">GrooScript 0.5.1</a> with some bug fixes</li>
<li>Spring Tool Suite &amp; <a href="http://docs.spring.io/sts/nan/v360/NewAndNoteworthy-M1.html">Groovy/Grails Tool Suite 3.6.0.M1</a>: with Groovy 2.3, Grails 2.4, and Gradle tooling</li>
<li>Version <a href="https://twitter.com/noamtenne/status/473059113350750208">0.0.1.6 of sGVM</a> has been released, adding support for Glide</li>
<li><a href="http://www.ratpack.io/versions/0.9.5">Ratpack 0.9.5</a> is available</li>
<li><a href="http://pleasingsoftware.blogspot.dk/2014/06/groovyfx-040-released.html">GroovyFX 0.4.0</a> released</li>
<li>Marco Vermeulen released <a href="https://twitter.com/marcovermeulen/status/473580701296508928">v0.5.6 of the GVM SDK</a></li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Guillaume Laforge shows the <a href="https://glaforge.dev/posts/2014/06/02/apple-s-swift-programming-language-inspired-by-groovy/">similitudes between Groovy and Apple’s Swift</a> new programming language for iOS</li>
<li>Cédric Champeau writes about how to <a href="https://spring.io/blog/2014/05/28/using-the-innovative-groovy-template-engine-in-spring-boot">use Groovy 2.3&rsquo;s new markup template engine in Spring Boot</a></li>
<li><a href="http://www.objectpartners.com/2014/05/29/unit-test-your-server-side-javascript-with-spock/">Unit test your serverside JavaScript with Spock</a> by David Norton</li>
<li>Alex Staveley shares a Grails tip about how to <a href="http://dublintech.blogspot.dk/2014/05/grails-tip-refactoring-your-urls.html">refactor your URLs</a></li>
<li>Tomás Lin&rsquo;s <a href="http://fbflex.wordpress.com/2014/06/02/spring-boot-recipe-reading-and-validating-lists-of-configuration-properties/">Spring Boot recipe: reading and validating lists of configuration properties</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>MrHaki shares the notes and slides of his <a href="https://twitter.com/mrhaki/status/473420460399800321">Groovy introduction workshop</a> at GR8Conf Europe</li>
<li>Álvaro Sánchez-Mariscal published his GR8Conf Europe presentation on <a href="http://fr.slideshare.net/alvarosanchezmariscal/restful-gr8conf2014">creating restful APIs with Grails and Spring Security</a></li>
<li>If you fancy some French, Guillaume Laforge’s presentation on <a href="http://parleys.com/play/538c3063e4b0a38a46cbec99/chapter0/about">what makes Groovy groovy</a> is online on Parleys</li>
<li>Andrés Almiray shares his <a href="http://fr.slideshare.net/aalmiray/gr8conf-groovy-ecosystem">Groovy Ecosystem</a> presentation from GR8Conf Europe</li>
<li>Jorge Franco Leza uploaded his GR8Conf Europe <a href="https://twitter.com/jfrancoleza/status/473807095088037888">presentation about GrooScript</a>, the Groovy to JavaScript compiler</li>
<li>Ryan Vanderwerf put online his <a href="https://twitter.com/ryanvanderwerf/status/473814334586761216">Groovy + Lego mindstorms slides and samples</a> from GR8Conf Europe</li>
<li>Robert Fletcher talked about <a href="http://freeside.co/hybrid-views-with-grails/assets/player/KeynoteDHTMLPlayer.html#0">hybrid views with Grails</a> at GR8Conf Europe</li>
<li>At GR8Conf Europe, Benoit Hédiard presented about <a href="https://speakerdeck.com/benorama/running-a-lean-startup-devops-and-grails-and-aws">Lean Startup, DevOps with Grails and Amazon Web Services</a></li>
</ul>
<h2 id="mailing-list-posts">Mailing-list posts</h2>
<ul>
<li>Cédric Champeau notes that JDK 7u60 is eventually the <a href="http://groovy.329449.n5.nabble.com/JDK7u60-is-out-for-indy-lovers-td5719838.html">first JDK 7 update release with correct invoke dynamic support</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li><a href="https://twitter.com/pledbrook/status/471545892231995394">Groovy Podcast, episode 2</a>, by Peter Ledbrook, and the GR8Ladies</li>
<li>Jon Brisbin launched the <a href="http://projectreactor.org/">new Reactor website</a></li>
<li>Gradleware’s May <a href="http://www.gradleware.com/newsletter/gradleware-newsletter-may-2014-2/">newsletter</a></li>
</ul>
<h2 id="code-contributions">Code contributions</h2>
<ul>
<li>The Ratpack project shares a possible contribution, for <a href="https://twitter.com/ratpackweb/status/472262669970337792">implementing a redirect() method for chains</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Russell Hart is showing how to <a href="https://twitter.com/rus_hart/status/473186395826434048">use Hystrix in Ratpack applications</a></li>
<li>Kenneth Endfinger gives examples of the <a href="https://github.com/kaendfinger/hardcore-groovy/blob/master/src/spread.groovy.md">Groovy spread operator</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Pieter Niederwieser notes that Apple’s new programming language, <a href="https://twitter.com/pniederw/status/473571956441489408">Swift, looks a lot like Groovy</a></li>
<li><a href="https://twitter.com/gmcconnaughey/status/473580913234690049">Groovy mentioned during the Apple presentations about Swift</a>, their new programming language</li>
<li>During his GR8Conf Europe keynote, Guillaume Laforge started with a little <a href="https://twitter.com/ilopmar/status/473724325653385216">comparison between Apple&rsquo;s Swift and Groovy</a></li>
<li>Guillaume Laforge updated the <a href="https://twitter.com/glaforge/status/473396838125830144">Groovy Web Console with Groovy 2.3.2</a> (including traits, etc), and latest Gaelyk and App Engine SDK</li>
<li>Graeme Rocher is working on a <a href="https://twitter.com/graemerocher/status/471582885204733952">proof of concept of Grails 3.0 on top of Spring Boot</a></li>
<li><a href="https://twitter.com/gvmtool/status/471575702148771840">Groovy 2.3.2</a> is available on GVM</li>
<li>The <a href="https://twitter.com/gr8conf/status/471599562210418688">GR8Conf europe official beers</a>!</li>
<li><a href="https://twitter.com/andreyhihlovski/status/472036112362651649">New location for Gretty documentation</a> (the Jetty Gradle plugin), generated with JBake and Asciidoctor</li>
<li>An ecstatic tweet by Omar Khan on how <a href="https://twitter.com/recoveringjahil/status/472143758570831872">beautiful Groovy is, and how awesome Grails</a> is</li>
<li>More <a href="https://twitter.com/ratpackweb/status/472263360491180033">integration between Ratpack and Reactor</a></li>
<li><a href="https://twitter.com/dailygrailstip/status/472085178899132416">gHipster</a> is a version of the jjipster Spring Boot framework that uses Gradle instead of Maven. Also Spock.</li>
<li>but <a href="http://jhipster.github.io/2014/06/02/jhipster-release-0.16.0.html">jhipster is actually adding Gradle</a> support</li>
<li><a href="https://twitter.com/mrundberget/status/473054214273642496">Visualize your Gradle project dependencies graphically</a> with the Light Table Groovy plugin</li>
<li><a href="https://twitter.com/gvmtool/status/473403577097261056">Spring Boot 1.1.0 RC 1</a> is available on GVM</li>
<li>Andrés Almiray published <a href="https://twitter.com/aalmiray/status/473792051293982720">pictures of the GR8Conf Hackergarten</a></li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>The great <a href="https://twitter.com/mrhaki/status/473134274582179840">Groovy and Grails Goodness books</a> from MrHaki are half the price during GR8Conf Europe!</li>
<li>Today only, there’s <a href="https://twitter.com/bmuschko/status/473894079508791296">50% off Gradle in Action</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>The <a href="https://storify.com/glaforge/gr8conf-us-upcoming-talks">GR8Conf US crew is tweeting about the interesting talks</a> we&rsquo;ll get to see in Minneapolis this summer</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Apple's Swift programming language inspired by Groovy</title><link>https://glaforge.dev/posts/2014/06/02/apple-s-swift-programming-language-inspired-by-groovy/</link><pubDate>Mon, 02 Jun 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/06/02/apple-s-swift-programming-language-inspired-by-groovy/</guid><description>&lt;p>During Apple&amp;rsquo;s &lt;a href="https://developer.apple.com/wwdc/">WWDC&lt;/a> conference was announced a new programming language, called &lt;a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.html#//apple_ref/doc/uid/TP40014097-CH2-XID_1">Swift&lt;/a>, targeted at developing on iOS devices (and likely Mac OS X in the future as well).&lt;/p>
&lt;p>When looking through the slides from the keynote, &lt;a href="https://developer.apple.com/library/prerelease/ios/referencelibrary/GettingStarted/LandingPage/index.html#//apple_ref/doc/uid/TP40014345">online documentation&lt;/a>, and &lt;a href="https://itun.es/fr/jEUH0.l">iTunes ebook&lt;/a>, an acquainted Groovy eye would immediately notice the inspiration the Swift designers took from Groovy.&lt;/p>
&lt;p>In one of the slides covering Swift, Groovy was even mentioned as one of the languages clearly making developers much more productive, although I disagree with the placement of Groovy in terms of performance as Groovy should clearly be leading the pack of the bottom right hand quadrant.&lt;br />
&lt;figure>
&lt;a href="#img-8e2bda7d8f8f6637e31cdecb3cffba28">
&lt;img src="https://glaforge.dev/img/misc/swift.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-8e2bda7d8f8f6637e31cdecb3cffba28">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/swift.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p></description><content:encoded>
<![CDATA[<p>During Apple&rsquo;s <a href="https://developer.apple.com/wwdc/">WWDC</a> conference was announced a new programming language, called <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.html#//apple_ref/doc/uid/TP40014097-CH2-XID_1">Swift</a>, targeted at developing on iOS devices (and likely Mac OS X in the future as well).</p>
<p>When looking through the slides from the keynote, <a href="https://developer.apple.com/library/prerelease/ios/referencelibrary/GettingStarted/LandingPage/index.html#//apple_ref/doc/uid/TP40014345">online documentation</a>, and <a href="https://itun.es/fr/jEUH0.l">iTunes ebook</a>, an acquainted Groovy eye would immediately notice the inspiration the Swift designers took from Groovy.</p>
<p>In one of the slides covering Swift, Groovy was even mentioned as one of the languages clearly making developers much more productive, although I disagree with the placement of Groovy in terms of performance as Groovy should clearly be leading the pack of the bottom right hand quadrant.<br />
<figure>
  <a href="#img-8e2bda7d8f8f6637e31cdecb3cffba28">
    <img src="/img/misc/swift.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-8e2bda7d8f8f6637e31cdecb3cffba28">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/swift.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Swift is clearly a blend of various good things from many existing languages:</p>
<ul>
<li>the getter / setter syntax close to the one from C#</li>
<li>the type after variable named and colon, like in Pascal-derived languages</li>
<li>string interpolation like in many scripting languages, using <code>\(foo)</code> versus Groovy&rsquo;s <code>${foo}</code></li>
<li>the question mark suffix after a type to denote it can be nullable, like in Ceylon</li>
<li>the range operators .. and &hellip; borrowed from Ruby (that Groovy also adopted and adapted for clarity, as Groovy&rsquo;s excluded upper bound is denoted by <code>..&lt;</code> instead)</li>
</ul>
<p>But Swift clearly took inspiration from Groovy on the following aspects.</p>
<p>Groovy and Swift (as well as other scripting languages) do have that concept of script, where free-standing statements don&rsquo;t need to be bundled into some kind of class structure, making scripting languages nice for the REPL feedback loop.</p>
<p>The syntax for lists and maps is the same in Swift and Groovy, with a comma-separated list of values surrounded by square brackets for lists, and key:value pairs comma-separated and surrounded again by square brackets for creating maps. The empty map is also the same with the [:] syntax.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Swift
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">var</span> shoppingList <span style="color:#666">=</span> <span style="color:#666">[</span><span style="color:#4070a0">&#34;catfish&#34;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#34;water&#34;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#34;tulips&#34;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#34;blue paint&#34;</span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>shoppingList<span style="color:#666">[</span><span style="color:#40a070">1</span><span style="color:#666">]</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#34;bottle of water&#34;</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#902000">var</span> occupations <span style="color:#666">=</span> <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;Malcolm&#34;</span><span style="color:#666">:</span> <span style="color:#4070a0">&#34;Captain&#34;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;Kaylee&#34;</span><span style="color:#666">:</span> <span style="color:#4070a0">&#34;Mechanic&#34;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>occupations<span style="color:#666">[</span><span style="color:#4070a0">&#34;Jayne&#34;</span><span style="color:#666">]</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Public Relations&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">var</span> emptyMap <span style="color:#666">=</span> <span style="color:#666">[:]</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">var</span> emptyList <span style="color:#666">=</span> <span style="color:#666">[]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Groovy
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> shoppingList <span style="color:#666">=</span> <span style="color:#666">[</span><span style="color:#4070a0">&#34;catfish&#34;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#34;water&#34;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#34;tulips&#34;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#34;blue paint&#34;</span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>shoppingList<span style="color:#666">[</span><span style="color:#40a070">1</span><span style="color:#666">]</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#34;bottle of water&#34;</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> occupations <span style="color:#666">=</span> <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;Malcolm&#34;</span><span style="color:#666">:</span> <span style="color:#4070a0">&#34;Captain&#34;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;Kaylee&#34;</span><span style="color:#666">:</span> <span style="color:#4070a0">&#34;Mechanic&#34;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>occupations<span style="color:#666">[</span><span style="color:#4070a0">&#34;Jayne&#34;</span><span style="color:#666">]</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Public Relations&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> emptyMap <span style="color:#666">=</span> <span style="color:#666">[:]</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> emptyList <span style="color:#666">=</span> <span style="color:#666">[]</span>
</span></span></code></pre></div><p>The sole difference here is actually the fact Groovy&rsquo;s using &ldquo;def&rdquo; instead of &ldquo;var&rdquo;. But a nice aspect of Swift is that it&rsquo;s got a &ldquo;let&rdquo; keyword for defining constants. You can use &ldquo;final&rdquo; in Groovy but it&rsquo;s not enforced by the compiler (yet).</p>
<p>Swift closures also adopt the same syntax as Groovy for its curly braces delimiters, but is using an &ldquo;in&rdquo; keyword to replace Groovy&rsquo;s arrow separating parameters from closure&rsquo;s body.</p>
<p>Here&rsquo;s what an example of Swift closure looks like compared to Groovy:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// swift
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>numbers<span style="color:#666">.</span><span style="color:#4070a0">map</span><span style="color:#666">({</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">(</span><span style="color:#002070;font-weight:bold">number:</span> Int<span style="color:#666">)</span> <span style="color:#666">-&gt;</span> Int <span style="color:#007020;font-weight:bold">in</span>
</span></span><span style="display:flex;"><span>    let result <span style="color:#666">=</span> <span style="color:#40a070">3</span> <span style="color:#666">*</span> number
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">return</span> result
</span></span><span style="display:flex;"><span><span style="color:#666">})</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// groovy
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>numbers<span style="color:#666">.</span><span style="color:#4070a0">collect</span> <span style="color:#666">{</span> <span style="color:#902000">int</span> number <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> result <span style="color:#666">=</span> <span style="color:#40a070">3</span> <span style="color:#666">*</span> numbers
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">return</span> result
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>But there&rsquo;s also a shorter variant for both languages, with Swift&rsquo;s positional closure parameters, versus Groovy&rsquo;s &ldquo;it&rdquo; pseudo-keyword:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// swift
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>numbers<span style="color:#666">.</span><span style="color:#4070a0">map</span><span style="color:#666">({</span> <span style="color:#40a070">3</span> <span style="color:#666">*</span> $0 <span style="color:#666">})</span>
</span></span><span style="display:flex;"><span>numbers<span style="color:#666">.</span><span style="color:#4070a0">map</span><span style="color:#666">({</span> it <span style="color:#007020;font-weight:bold">in</span> <span style="color:#40a070">3</span> <span style="color:#666">*</span> it <span style="color:#666">})</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// groovy
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>numbers<span style="color:#666">.</span><span style="color:#4070a0">collect</span> <span style="color:#666">{</span> <span style="color:#40a070">3</span> <span style="color:#666">*</span> it <span style="color:#666">}</span>
</span></span></code></pre></div><p>Swift also adopted Groovy&rsquo;s syntactic rule that if a closure is the last parameter of a function or method, you can actually put that last parameter outside the parentheses like so:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// swift
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>sort<span style="color:#666">([</span><span style="color:#40a070">1</span><span style="color:#666">,</span> <span style="color:#40a070">5</span><span style="color:#666">,</span> <span style="color:#40a070">3</span><span style="color:#666">,</span> <span style="color:#40a070">12</span><span style="color:#666">,</span> <span style="color:#40a070">2</span><span style="color:#666">])</span> <span style="color:#666">{</span> $0 <span style="color:#666">&gt;</span> $1 <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// groovy
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>sort<span style="color:#666">([</span><span style="color:#40a070">1</span><span style="color:#666">,</span> <span style="color:#40a070">5</span><span style="color:#666">,</span> <span style="color:#40a070">3</span><span style="color:#666">,</span> <span style="color:#40a070">12</span><span style="color:#666">,</span> <span style="color:#40a070">2</span><span style="color:#666">])</span> <span style="color:#666">{</span> a<span style="color:#666">,</span> b <span style="color:#666">-&gt;</span> a <span style="color:#666">&gt;</span> b <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// or possibly, if we pass an array as param
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>sort<span style="color:#666">([</span><span style="color:#40a070">1</span><span style="color:#666">,</span> <span style="color:#40a070">5</span><span style="color:#666">,</span> <span style="color:#40a070">3</span><span style="color:#666">,</span> <span style="color:#40a070">12</span><span style="color:#666">,</span> <span style="color:#40a070">2</span><span style="color:#666">])</span> <span style="color:#666">{</span> it<span style="color:#666">[</span><span style="color:#40a070">0</span><span style="color:#666">]</span> <span style="color:#666">&gt;</span> it<span style="color:#666">[</span><span style="color:#40a070">1</span><span style="color:#666">]</span> <span style="color:#666">}</span>
</span></span></code></pre></div><p>Another interesting aspect is named parameters. For instance, for instantiating objects:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// swift
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">var</span> triangleAndSquare <span style="color:#666">=</span> TriangleAndSquare<span style="color:#666">(</span><span style="color:#002070;font-weight:bold">size:</span> <span style="color:#40a070">10</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">name:</span> <span style="color:#4070a0">&#34;another test shape&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// groovy
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> triangleAndSquare <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> TriangleAndSquare<span style="color:#666">(</span><span style="color:#002070;font-weight:bold">size:</span> <span style="color:#40a070">10</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">name:</span> <span style="color:#4070a0">&#34;another test shape&#34;</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>Swift has a <code>@lazy</code> annotation similar to Groovy&rsquo;s <code>@Lazy</code> code transformation, to lazily instantiate complex fields only when they are first accessed:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// swift
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">DataManager</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@lazy</span> <span style="color:#902000">var</span> importer <span style="color:#666">=</span> DataImporter<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// groovy
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">DataManager</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Lazy</span> importer <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> DataImporter<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Alongside its nullable types à la Ceylon, Swift adopted the safe navigation operator of Groovy (the next version of C# is going to support that Groovy notation as well):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// swift
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>let john <span style="color:#666">=</span> Person<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">if</span> let roomCount <span style="color:#666">=</span> john<span style="color:#666">.</span><span style="color:#4070a0">residence</span><span style="color:#666">?.</span><span style="color:#4070a0">numberOfRooms</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    println<span style="color:#666">(</span><span style="color:#4070a0">&#34;John&#39;s residence has \(roomCount) room(s).&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">else</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    println<span style="color:#666">(</span><span style="color:#4070a0">&#34;Unable to retrieve the number of rooms.&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// groovy
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> john <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Person<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> roomCount <span style="color:#666">=</span> john<span style="color:#666">.</span><span style="color:#4070a0">residence</span><span style="color:#666">?.</span><span style="color:#4070a0">numberOfRooms</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>roomCount<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    println <span style="color:#4070a0">&#34;John&#39;s residence has ${roomCount} room(s).&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">else</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    println <span style="color:#4070a0">&#34;Unable to retrieve the number of rooms.&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Swift is brand new, and I haven&rsquo;t had much time to dive deeper yet, but it sounds like a nice and elegant language, and right out of the box, it makes me feel at home thanks to its resemblance with Groovy! It even makes me want to write Swift applications for some iOS device!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.3.2 released</title><link>https://glaforge.dev/posts/2014/05/28/groovy-2-3-2-released/</link><pubDate>Wed, 28 May 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/05/28/groovy-2-3-2-released/</guid><description>&lt;p>The Groovy team is pleased to announce the release of Groovy 2.3.2, a bug fix release for our 2.3 main branch.&lt;/p>
&lt;p>If you have already moved to Groovy 2.3, we strongly recommend you to upgrade to 2.3.2 rapidly, as our two prior 2.3 releases were using a buggy version of ASM which was generating wrong bytecode for inner classes.&lt;/p>
&lt;p>You can check this issue for more details: &lt;a href="https://jira.codehaus.org/browse/GROOVY-6808">https://jira.codehaus.org/browse/GROOVY-6808&lt;/a>&lt;/p>
&lt;p>Since any code compiled with Groovy 2.3.0 or 2.3.1 and using inner classes is likely to have faulty bytecode, please make sure to recompile everything with 2.3.2&lt;br />
Our detailed JIRA change log: &lt;a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;version=20402">http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;version=20402&lt;/a>&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy team is pleased to announce the release of Groovy 2.3.2, a bug fix release for our 2.3 main branch.</p>
<p>If you have already moved to Groovy 2.3, we strongly recommend you to upgrade to 2.3.2 rapidly, as our two prior 2.3 releases were using a buggy version of ASM which was generating wrong bytecode for inner classes.</p>
<p>You can check this issue for more details: <a href="https://jira.codehaus.org/browse/GROOVY-6808">https://jira.codehaus.org/browse/GROOVY-6808</a></p>
<p>Since any code compiled with Groovy 2.3.0 or 2.3.1 and using inner classes is likely to have faulty bytecode, please make sure to recompile everything with 2.3.2<br />
Our detailed JIRA change log: <a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=20402">http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=20402</a></p>
<p>And go download Groovy at the usual place: <a href="http://groovy.codehaus.org/Download?nc">http://groovy.codehaus.org/Download?nc</a></p>
<p>Thanks to all who contributed!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #23</title><link>https://glaforge.dev/posts/2014/05/27/groovy-weekly-23/</link><pubDate>Tue, 27 May 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/05/27/groovy-weekly-23/</guid><description>&lt;p>The big news of this edition is the release of Grails 2.4, with standalone GORM and GSP, with more static compilation that even understands dynamic aspects of Grails, with a new asset pipeline, with GORM sub-queries, and with a new Maven plugin.&lt;/p>
&lt;p>The other big news is that next week is the GR8Conf Europe week! So you’ll certainly see lots of nice and interesting presentations being put online next week. And I hope I’ll also be able to publish this weekly news brief in time too!&lt;/p></description><content:encoded>
<![CDATA[<p>The big news of this edition is the release of Grails 2.4, with standalone GORM and GSP, with more static compilation that even understands dynamic aspects of Grails, with a new asset pipeline, with GORM sub-queries, and with a new Maven plugin.</p>
<p>The other big news is that next week is the GR8Conf Europe week! So you’ll certainly see lots of nice and interesting presentations being put online next week. And I hope I’ll also be able to publish this weekly news brief in time too!</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Graeme Rocher announces the final <a href="https://spring.io/blog/2014/05/21/grails-2-4-released">release of Grails 2.4</a></li>
<li><a href="https://grails.org/2.3.9+Release+Notes">Grails 2.3.9</a> released</li>
<li><a href="https://github.com/rundis/LightTable-Groovy/releases/tag/0.0.5">LightTable Groovy 0.0.5</a> with Gradle task execution</li>
<li><a href="https://twitter.com/andreyhihlovski/status/469781264048746496">Gretty v0.0.20</a>, the Grable plugin for Jetty, by Andrey Hihlovskiy</li>
<li>Paolo Di Tommaso releases <a href="http://nextflow.io">Nextflow 0.0.8</a>, a workflow engine based on GPars dataflow, now including more splitting operators, updated to Groovy 2.3</li>
<li>Thibault Kruse released the <a href="https://github.com/tkruse/gradle-groovysh-plugin">Groovysh Gradle plugin</a>, to be able to launch Groovysh from your Gradle builds</li>
<li><a href="https://spring.io/blog/2014/05/27/spring-boot-1-1-0-m2-available-now">Spring Boot 1.1.0.M2</a> released by Dave Syer</li>
<li>Stergios Papadimitriou is adding <a href="https://code.google.com/p/jlabgroovy/wiki/SciLabInGroovyLab">SciLab integration to JLabGroovy</a></li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Graeme Rocher explains how to <a href="http://grails.io/post/86513009278/publishing-a-pom-with-gradle">publish a POM with Gradle</a></li>
<li>Lots of MrHaki’s Goodness!</li>
<li>MrHaki&rsquo;s Groovy Goodness: <a href="http://mrhaki.blogspot.fr/2014/05/groovy-goodness-implementing-traits-at.html">Implementing Traits at Runtime</a></li>
<li>MrHaki&rsquo;s Groovy Goodness: <a href="http://mrhaki.blogspot.fr/2014/05/groovy-goodness-chaining-traits.html">Chaining Traits</a></li>
<li>MrHaki&rsquo;s Grails Goodness: <a href="http://mrhaki.blogspot.fr/2014/05/grails-goodness-exception-methods-in.html">Exception Methods in Controllers</a></li>
<li>MrHaki&rsquo;s Grails Goodness: <a href="http://mrhaki.blogspot.fr/2014/05/grails-goodness-run-groovy-scripts-in.html">Run Groovy Scripts in Grails Context</a></li>
<li>Broadleaf with <a href="http://www.broadleafcommerce.com/blog/broadleaf-with-groovy-and-gradle-part-1">Groovy and Gradle</a> — Part 1</li>
<li>Broadleaf with <a href="http://www.broadleafcommerce.com/blog/broadleaf-with-groovy-and-gradle-part-2">Groovy and Gradle</a> — Part 2</li>
<li><a href="http://spinscale.github.io/2013-02-rapid-web-application-development-with-spark-groovy-jrebel.html">Rapid application development with Spark, Groovy and JRebel</a> by Alexander Reelsen</li>
<li>A <a href="http://wicketinaction.com/2014/05/groovy-dsl-for-wicket/">Groovy DSL for Apache Wicket</a></li>
<li>An infographic on <a href="http://www.cygnet-infotech.com/infographic-6-reasons-why-grails-is-an-awesome-java-web-framework">6 reasons why Grails is an awesome framework</a></li>
<li>Magnus Rundberget blogs about exploring <a href="http://codewader.blogspot.no/2014/05/a-groovy-light-table-client-step-4.html">new Gradle avenues with the Groovy Light Table</a> client</li>
<li><a href="http://www.javacodegeeks.com/2014/05/grails-2-4-released-installing-groovy-eclipse-compiler-2-3-in-eclipseggts.html">Installing Groovy-Eclipse Compiler 2.3</a> in Eclipse / GGTS</li>
<li><a href="http://www.objectpartners.com/2014/05/22/run-grails-commands-on-heroku/">Run Grails Commands on Heroku</a> by Brandon Fish</li>
<li>Tomás Lin writes about how to <a href="http://fbflex.wordpress.com/2014/05/24/adding-an-optional-internal-artifactory-repository-to-gradle-to-speed-up-builds/">add an optional internal Artifactory repository to Gradle</a> to speed up builds</li>
<li><a href="http://www.oodlestechnologies.com/blogs/Integrate-Markdown-plugin-in-Grail">Integrate Markdown plugin in Grails</a> by Deepak Gupta</li>
<li>Iyobo Eki on <a href="http://deveki.com/softdev/grails-shiro-not-redirecting-to-login-page/">Grails: Shiro not redirecting to login page</a></li>
<li>Iyobo Eki about <a href="http://deveki.com/softdev/grails-how-to-convert-html-templates-to-asset-pipeline-layouts/">Grails: 3 Simple steps to convert HTML templates into asset-pipeline layouts</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li><a href="http://www.oodlestechnologies.com/blogs/Integrate-Markdown-plugin-in-Grail">Guillaume Laforge p</a>resents <a href="http://www.parleys.com/play/536c9780e4b0898c64d2e43e/chapter1/about">Gr</a><a href="http://www.oodlestechnologies.com/blog/filterByAuthor?author=deepakgupta">oovy in 2014</a> <a href="http://www.parleys.com/play/536c9780e4b0898c64d2e43e/chapter1/about">and beyond</a>, covering Groovy 2.3, in French, recorded at Devoxx France 2014</li>
<li>Cédric Champeau explains how <a href="http://www.parleys.com/play/53676fb0e4b0593229b8583e/chapter1/about">Gradle doesn’t just replace Maven</a>, but goes much beyond, in French, recorded at Devoxx France 2014</li>
</ul>
<h2 id="screencasts">Screencasts</h2>
<ul>
<li>Magnus Rundberget shows a <a href="https://twitter.com/mrundberget/status/469619079926808577">preview of the Gradle task integration in the Light Table Groovy plugin</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>GradleWare features <a href="http://www.gradleware.com/services/training/gradle-virtual-training/">online Gradle training</a></li>
<li>Siemens releases a <a href="https://github.com/siemens/restapidoc/">Grails plugin for documenting RESTful APIs</a></li>
<li><a href="http://grydeske.net/news/show/46">Grails Diary week 21</a>, by Jacob Aae Mikkelsen</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Guillaume Laforge implemented an example of <a href="http://rosettacode.org/wiki/Huffman_coding#Groovy">Huffman code</a> in Groovy on Rosetta Code. You can create your own entries for the various puzzles in your favorite language!</li>
<li>Cédric Champeau implemented a <a href="https://gist.github.com/melix/8580bdc709cdddd05277">Hodor DSL</a> based on Game of Thrones’ Hodor character</li>
<li>Grant McConnaughey shares a <a href="https://gist.github.com/grantmcconnaughey/d37b733dfd70439e17f1">Bash script</a> that informs you when a Grails version is available on GVM</li>
<li>Peter Ledbrooks shares an example of a <a href="https://twitter.com/pledbrook/status/469382343112003584">multi-project Maven build for Grails</a> applications</li>
<li>A demo of <a href="https://twitter.com/grooscript/status/469948334862790659">Groovy compiled to JavaScript running in the clouds</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li><a href="https://twitter.com/aalmiray/status/469141716738777089">CERN is using Grails</a> for internal applications</li>
<li>Cédric Champeau shows that <a href="https://twitter.com/cedricchampeau/status/471338828876509184">Android development can be made much groovier</a>!</li>
<li><a href="https://twitter.com/gvmtool/status/469139587647176704">Grails 2.4</a> is available in GVM</li>
<li><a href="https://twitter.com/java_hipster/status/470885227121111041">Gradle support</a> is underway for the JHipster project generator for AngularJS / Spring Boot apps</li>
<li>Ken Kousen notes a needed <a href="https://twitter.com/kenkousen/status/469914000156016641">dependency change if you use Groovy’s AntBuilder in Gradle</a> when using Groovy 2.3</li>
<li>Cédric Champeau is working on an <a href="https://twitter.com/CedricChampeau/status/469404646516482048">Antlr v4 Gradle plugin</a></li>
<li>The <a href="https://twitter.com/vscenegis/status/471150776380051456">vscengis GIS (Geographic Information System) is using Groovy</a> for its scripting layer</li>
<li>Rob Fletcher remarks that IntelliJ IDEA has an <a href="https://twitter.com/rfletcherew/status/471310326537793536">intention to sort map entries alphabetically</a></li>
<li><a href="https://twitter.com/gvmtool/status/471361060772249600">Spring Boot 1.1.0.M2</a> available for download on GVM</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>A <a href="https://twitter.com/j_brisbin/status/469925169398829057">book on Reactor</a> is in the works at O’Reilly, by Jon Brisbin and Stéphane Maldini</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>If you&rsquo;re going to GR8Conf Europe, you can <a href="https://twitter.com/cedricchampeau/status/471270003678117888">download the GR8Conf Android app</a> to prepare your program</li>
<li>The <a href="https://twitter.com/gr8conf/status/471255330111127552">GR8Conf Europe official beers</a> are being bottled up!</li>
<li>The <a href="https://twitter.com/sbglasius/status/471269691621916672">second beer of GR8Conf Europe</a> is called “Malt Liquor”</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #22</title><link>https://glaforge.dev/posts/2014/05/20/groovy-weekly-22/</link><pubDate>Tue, 20 May 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/05/20/groovy-weekly-22/</guid><description>&lt;p>Releases keep coming, with a second RC for Grails 2.4, with Gaelyk and Griffon upgrading to Groovy 2.3, and already a bug-fix release of Groovy with version 2.3.1!&lt;/p>
&lt;p>Don’t forget it’s the last week to &lt;a href="http://gr8conf.eu">register to the GR8Conf Europe&lt;/a> conference!&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/grailsframework/status/466962419953446912">Second release candidate for Grails 2.4&lt;/a>&lt;/li>
&lt;li>Guillaume Laforge announces a &lt;a href="https://glaforge.dev/posts/2014/05/19/groovy-2-3-1-is-out/">Groovy 2.3.1 bug-fix release&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://gaelyk.appspot.com/download">Gaelyk 2.1.2&lt;/a> released, built atop Groovy 2.3.0&lt;/li>
&lt;li>Kunal Dabir released &lt;a href="https://twitter.com/kdabir/status/466281435892944896">version 2.1.2 of the Gaelyk template&lt;/a>&lt;/li>
&lt;li>Andrés Almiray is upgrading &lt;a href="https://twitter.com/aalmiray/status/466297367361515520">Griffon 2.0.0 beta&lt;/a> with Groovy 2.3&lt;/li>
&lt;li>The &lt;a href="http://spudengine.com/news/2014-05-15-spud-grails-0-4-x-released-with-tons-of-new-features">Spud CMS engine for Grails&lt;/a> released with new features&lt;/li>
&lt;li>The &lt;a href="https://github.com/rundis/LightTable-Groovy/releases/tag/0.0.4">Groovy Light Table client released version 0.0.4&lt;/a> with further Gradle integration&lt;/li>
&lt;li>&lt;a href="http://gmavenplus.56682.x6.nabble.com/gmavenplus-announce-GMavenPlus-1-2-Released-td137.html">GMavenPlus 1.2 released&lt;/a> for building your Groovy and Java projects with Maven&lt;/li>
&lt;/ul>
&lt;h2 id="interviews">Interviews&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://jaxenter.de/videos/What-makes-Groovy-groovy-Interview-mit-Guillaume-Laforge-173539">Guillaume Laforge interviewed at the JAX conference about what makes Groovy groovy&lt;/a>, the latest 2.3 release, and roadmap&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>At the JAX conference, Guillaume Laforge presented on &amp;ldquo;&lt;a href="https://twitter.com/glaforge/status/466889089628012544">what makes Groovy groovy&lt;/a>&amp;rdquo;&lt;/li>
&lt;li>Guillaume Laforge presented the &lt;a href="https://twitter.com/glaforge/status/466891844958572544">functional aspects of Groovy&lt;/a> at the JAX conference&lt;/li>
&lt;li>A screencast showing the &lt;a href="https://twitter.com/mrundberget/status/468193954123755520">Gradle integration in the Light Table Groovy&lt;/a> plugin&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>Mixing &lt;a href="http://vasya10.wordpress.com/2014/05/03/the-groovyspringbootbatchgormgroovydslbeanfactory/">Spring Boot, Groovy, Spring Batch, Grails&amp;rsquo; GORM&lt;/a> together&lt;/li>
&lt;li>An article &lt;a href="http://www.i-programmer.info/news/98-languages/7304-new-groovy.html">summarizing the latest release of Groovy 2.3&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.mscharhag.com/2014/05/grails-tomcat-kill-switch.html">Grails: The Tomcat kill switch&lt;/a>, by Michael Scharhag&lt;/li>
&lt;li>Michael Scharhag is blogging about &lt;a href="http://www.mscharhag.com/2014/05/grails-controller-namespaces.html">Grails Controller namespaces&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.intelligrape.com/blog/2014/05/13/grails-way-of-oauth-2-0-to-access-google-apis-part-1/">OAuth 2.0 using Grails&lt;/a>&lt;/li>
&lt;li>Brandon Fish explains the &lt;a href="http://www.objectpartners.com/2014/05/13/grails-r14-error-memory-quota-exceeded-on-heroku/">Grails R14 memory error on Heroku&lt;/a>&lt;/li>
&lt;li>Alex Staveley speaks about &lt;a href="http://dublintech.blogspot.ie/2014/05/the-magic-of-groovys-with.html">Groovy’s “with”&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.objectpartners.com/2014/05/15/automatically-test-your-dirty-grails-classes/">Automatically test your dirty Grails classes&lt;/a> by Igor Shults&lt;/li>
&lt;li>&lt;a href="http://www.intelligrape.com/blog/2014/05/14/receive-email-using-subethasmtp-the-local-smtp-server/">Receiving emails in a Grails&lt;/a> app by Akash Sethi&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>Kenneth Endfinger shows a &lt;a href="https://gist.github.com/kaendfinger/fdab7ab0627baaa67884">Node.js hello world written in Groovy&lt;/a> and compiled to JavaScript with GrooScript&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>Robert Fletcher is &lt;a href="https://twitter.com/rfletcherew/status/467255461126086656">ecstatic about Groovy’s closure coercion to “SAM” types and type inference&lt;/a>, as this makes for very low ceremony code&lt;/li>
&lt;li>Robert Fletcher remarks that you can’t use &lt;a href="https://twitter.com/rfletcherew/status/467307293408002048">@CompileStatic on Spock specifications&lt;/a>, but you can on their helper methods&lt;/li>
&lt;li>Vladimir Orany updated his &amp;ldquo;&lt;a href="https://twitter.com/musketyr/status/466545743168540674">Gliding Gaelyk&lt;/a>&amp;rdquo; presentation and demo app&lt;/li>
&lt;li>Magnus Rundberget is &lt;a href="https://twitter.com/mrundberget/status/467078036438134784">integrating Gradle in the Groovy support for the Light Table IDE&lt;/a>, showing an example of the Groovy Stream library&lt;/li>
&lt;li>Cédric Champeau wished that &lt;a href="https://twitter.com/cedricchampeau/status/466858251221749760">Groovy were taught more at school&lt;/a>&lt;/li>
&lt;li>Schalk Cronjé is looking into adding &lt;a href="https://twitter.com/ysb33r/status/464335761161326592">Amazon S3 support to Groovy VFS&lt;/a>&lt;/li>
&lt;li>Peter Ledbrook is wondering &lt;a href="https://twitter.com/pledbrook/status/468390877736763392">why the Spock testing framework doesn’t have a logo&lt;/a>&lt;/li>
&lt;li>Guillaume Laforge shows some &lt;a href="https://twitter.com/glaforge/status/468455234688544768/photo/1">Spock logo ideas&lt;/a> he proposed to Peter Niederwieser&lt;/li>
&lt;li>Dierk König notices that Java 8 features are a notable improvement when working with JavaFX, but it&amp;rsquo;s only with &lt;a href="https://twitter.com/mittie/status/468423252633980928">Groovy and GroovyFX that it is as slick&lt;/a>&lt;/li>
&lt;li>Robert Fletcher reports a &lt;a href="https://twitter.com/rfletcherew/status/468422318062985216">gotcha when using Spock with fields and JUnit rules&lt;/a>&lt;/li>
&lt;li>More fun from Rob Fletcher showing how he cleverly used &lt;a href="https://twitter.com/rfletcherew/status/468425797011660800">Groovy&amp;rsquo;s alias imports to change void into &amp;ldquo;should&amp;rdquo; in his Spock&lt;/a> specifications&lt;/li>
&lt;li>Baruch Sadogursky &lt;a href="https://twitter.com/jbaruch/status/468356911042027520">happily converted a painful 300 lines of Java code using the Maven Aether library into 20 lines of Groovy&lt;/a> code&lt;/li>
&lt;li>Cédric Champeau added &lt;a href="https://twitter.com/cedricchampeau/status/468438346474471424">“layouts” to the markup template engine&lt;/a> in Groovy 2.3.1&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/468485257654976512">Groovy 2.3.1&lt;/a> is available through GVM&lt;/li>
&lt;/ul>
&lt;h2 id="code-contributions">Code contributions&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau suggests possible &lt;a href="https://twitter.com/cedricchampeau/status/466243598254878720">improvements that can be contributed&lt;/a> by the community around the Groovy 2.3 markup template engine&lt;/li>
&lt;/ul>
&lt;h2 id="news">News&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://grydeske.net/news/show/44">Grails Diary&lt;/a> #19 by Jacob Aae Mikkelsen&lt;/li>
&lt;li>Luke Daley announces &lt;a href="https://twitter.com/ldaley/status/466884136754376704">Marcin Erdmann is becoming the new project lead of the Geb&lt;/a> project&lt;/li>
&lt;li>The &lt;a href="http://grydeske.net/news/show/45">Grails Diary&lt;/a> week 20 by Jacob Aae Mikkelsen&lt;/li>
&lt;li>Geb now offers &lt;a href="http://www.gebish.org/manual/snapshot/cloud-browsers.html#cloud_browser_testing">cloud browser testing&lt;/a> with SauceLabs and BrowserStack&lt;/li>
&lt;li>Beaker, a &lt;a href="http://beakernotebook.com/">data science laboratory with Groovy integration&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>Only a &lt;a href="https://twitter.com/gr8conf/status/468277209677844481">few days left to register to attend GR8Conf&lt;/a> Europe!&lt;/li>
&lt;li>If you’re a student, you can get &lt;a href="https://twitter.com/gr8conf/status/468634470661500928">discounts for GR8Conf Europe&lt;/a>&lt;/li>
&lt;li>There are currently &lt;a href="https://twitter.com/gr8conf/status/468745724696752128">19 countries represented by the GR8Conf attendees&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>Releases keep coming, with a second RC for Grails 2.4, with Gaelyk and Griffon upgrading to Groovy 2.3, and already a bug-fix release of Groovy with version 2.3.1!</p>
<p>Don’t forget it’s the last week to <a href="http://gr8conf.eu">register to the GR8Conf Europe</a> conference!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://twitter.com/grailsframework/status/466962419953446912">Second release candidate for Grails 2.4</a></li>
<li>Guillaume Laforge announces a <a href="https://glaforge.dev/posts/2014/05/19/groovy-2-3-1-is-out/">Groovy 2.3.1 bug-fix release</a></li>
<li><a href="http://gaelyk.appspot.com/download">Gaelyk 2.1.2</a> released, built atop Groovy 2.3.0</li>
<li>Kunal Dabir released <a href="https://twitter.com/kdabir/status/466281435892944896">version 2.1.2 of the Gaelyk template</a></li>
<li>Andrés Almiray is upgrading <a href="https://twitter.com/aalmiray/status/466297367361515520">Griffon 2.0.0 beta</a> with Groovy 2.3</li>
<li>The <a href="http://spudengine.com/news/2014-05-15-spud-grails-0-4-x-released-with-tons-of-new-features">Spud CMS engine for Grails</a> released with new features</li>
<li>The <a href="https://github.com/rundis/LightTable-Groovy/releases/tag/0.0.4">Groovy Light Table client released version 0.0.4</a> with further Gradle integration</li>
<li><a href="http://gmavenplus.56682.x6.nabble.com/gmavenplus-announce-GMavenPlus-1-2-Released-td137.html">GMavenPlus 1.2 released</a> for building your Groovy and Java projects with Maven</li>
</ul>
<h2 id="interviews">Interviews</h2>
<ul>
<li><a href="http://jaxenter.de/videos/What-makes-Groovy-groovy-Interview-mit-Guillaume-Laforge-173539">Guillaume Laforge interviewed at the JAX conference about what makes Groovy groovy</a>, the latest 2.3 release, and roadmap</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>At the JAX conference, Guillaume Laforge presented on &ldquo;<a href="https://twitter.com/glaforge/status/466889089628012544">what makes Groovy groovy</a>&rdquo;</li>
<li>Guillaume Laforge presented the <a href="https://twitter.com/glaforge/status/466891844958572544">functional aspects of Groovy</a> at the JAX conference</li>
<li>A screencast showing the <a href="https://twitter.com/mrundberget/status/468193954123755520">Gradle integration in the Light Table Groovy</a> plugin</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Mixing <a href="http://vasya10.wordpress.com/2014/05/03/the-groovyspringbootbatchgormgroovydslbeanfactory/">Spring Boot, Groovy, Spring Batch, Grails&rsquo; GORM</a> together</li>
<li>An article <a href="http://www.i-programmer.info/news/98-languages/7304-new-groovy.html">summarizing the latest release of Groovy 2.3</a></li>
<li><a href="http://www.mscharhag.com/2014/05/grails-tomcat-kill-switch.html">Grails: The Tomcat kill switch</a>, by Michael Scharhag</li>
<li>Michael Scharhag is blogging about <a href="http://www.mscharhag.com/2014/05/grails-controller-namespaces.html">Grails Controller namespaces</a></li>
<li><a href="http://www.intelligrape.com/blog/2014/05/13/grails-way-of-oauth-2-0-to-access-google-apis-part-1/">OAuth 2.0 using Grails</a></li>
<li>Brandon Fish explains the <a href="http://www.objectpartners.com/2014/05/13/grails-r14-error-memory-quota-exceeded-on-heroku/">Grails R14 memory error on Heroku</a></li>
<li>Alex Staveley speaks about <a href="http://dublintech.blogspot.ie/2014/05/the-magic-of-groovys-with.html">Groovy’s “with”</a></li>
<li><a href="http://www.objectpartners.com/2014/05/15/automatically-test-your-dirty-grails-classes/">Automatically test your dirty Grails classes</a> by Igor Shults</li>
<li><a href="http://www.intelligrape.com/blog/2014/05/14/receive-email-using-subethasmtp-the-local-smtp-server/">Receiving emails in a Grails</a> app by Akash Sethi</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Kenneth Endfinger shows a <a href="https://gist.github.com/kaendfinger/fdab7ab0627baaa67884">Node.js hello world written in Groovy</a> and compiled to JavaScript with GrooScript</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Robert Fletcher is <a href="https://twitter.com/rfletcherew/status/467255461126086656">ecstatic about Groovy’s closure coercion to “SAM” types and type inference</a>, as this makes for very low ceremony code</li>
<li>Robert Fletcher remarks that you can’t use <a href="https://twitter.com/rfletcherew/status/467307293408002048">@CompileStatic on Spock specifications</a>, but you can on their helper methods</li>
<li>Vladimir Orany updated his &ldquo;<a href="https://twitter.com/musketyr/status/466545743168540674">Gliding Gaelyk</a>&rdquo; presentation and demo app</li>
<li>Magnus Rundberget is <a href="https://twitter.com/mrundberget/status/467078036438134784">integrating Gradle in the Groovy support for the Light Table IDE</a>, showing an example of the Groovy Stream library</li>
<li>Cédric Champeau wished that <a href="https://twitter.com/cedricchampeau/status/466858251221749760">Groovy were taught more at school</a></li>
<li>Schalk Cronjé is looking into adding <a href="https://twitter.com/ysb33r/status/464335761161326592">Amazon S3 support to Groovy VFS</a></li>
<li>Peter Ledbrook is wondering <a href="https://twitter.com/pledbrook/status/468390877736763392">why the Spock testing framework doesn’t have a logo</a></li>
<li>Guillaume Laforge shows some <a href="https://twitter.com/glaforge/status/468455234688544768/photo/1">Spock logo ideas</a> he proposed to Peter Niederwieser</li>
<li>Dierk König notices that Java 8 features are a notable improvement when working with JavaFX, but it&rsquo;s only with <a href="https://twitter.com/mittie/status/468423252633980928">Groovy and GroovyFX that it is as slick</a></li>
<li>Robert Fletcher reports a <a href="https://twitter.com/rfletcherew/status/468422318062985216">gotcha when using Spock with fields and JUnit rules</a></li>
<li>More fun from Rob Fletcher showing how he cleverly used <a href="https://twitter.com/rfletcherew/status/468425797011660800">Groovy&rsquo;s alias imports to change void into &ldquo;should&rdquo; in his Spock</a> specifications</li>
<li>Baruch Sadogursky <a href="https://twitter.com/jbaruch/status/468356911042027520">happily converted a painful 300 lines of Java code using the Maven Aether library into 20 lines of Groovy</a> code</li>
<li>Cédric Champeau added <a href="https://twitter.com/cedricchampeau/status/468438346474471424">“layouts” to the markup template engine</a> in Groovy 2.3.1</li>
<li><a href="https://twitter.com/gvmtool/status/468485257654976512">Groovy 2.3.1</a> is available through GVM</li>
</ul>
<h2 id="code-contributions">Code contributions</h2>
<ul>
<li>Cédric Champeau suggests possible <a href="https://twitter.com/cedricchampeau/status/466243598254878720">improvements that can be contributed</a> by the community around the Groovy 2.3 markup template engine</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li><a href="http://grydeske.net/news/show/44">Grails Diary</a> #19 by Jacob Aae Mikkelsen</li>
<li>Luke Daley announces <a href="https://twitter.com/ldaley/status/466884136754376704">Marcin Erdmann is becoming the new project lead of the Geb</a> project</li>
<li>The <a href="http://grydeske.net/news/show/45">Grails Diary</a> week 20 by Jacob Aae Mikkelsen</li>
<li>Geb now offers <a href="http://www.gebish.org/manual/snapshot/cloud-browsers.html#cloud_browser_testing">cloud browser testing</a> with SauceLabs and BrowserStack</li>
<li>Beaker, a <a href="http://beakernotebook.com/">data science laboratory with Groovy integration</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>Only a <a href="https://twitter.com/gr8conf/status/468277209677844481">few days left to register to attend GR8Conf</a> Europe!</li>
<li>If you’re a student, you can get <a href="https://twitter.com/gr8conf/status/468634470661500928">discounts for GR8Conf Europe</a></li>
<li>There are currently <a href="https://twitter.com/gr8conf/status/468745724696752128">19 countries represented by the GR8Conf attendees</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.3.1 is out</title><link>https://glaforge.dev/posts/2014/05/19/groovy-2-3-1-is-out/</link><pubDate>Mon, 19 May 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/05/19/groovy-2-3-1-is-out/</guid><description>&lt;p>The Groovy development team is happy to announce the bug fix &lt;strong>release of Groovy 2.3.1&lt;/strong>.&lt;/p>
&lt;p>Lots of bugs have been reported and fixed.&lt;/p>
&lt;p>Some of the highlights among those fixes:&lt;/p>
&lt;ul>
&lt;li>the classloader related problems with &lt;code>@Grab&lt;/code>&lt;/li>
&lt;li>several generics handling issues&lt;/li>
&lt;li>a problem preventing Groovy from loading its modules on IBM mainframes&lt;/li>
&lt;li>a problem preventing Groovy from being loaded as an OSGi module in certain OSGi containers&lt;/li>
&lt;/ul>
&lt;p>You can read the &lt;a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;version=20366">JIRA release notes&lt;/a>, and &lt;a href="http://groovy.codehaus.org/Download">download&lt;/a> the latest distribution.&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy development team is happy to announce the bug fix <strong>release of Groovy 2.3.1</strong>.</p>
<p>Lots of bugs have been reported and fixed.</p>
<p>Some of the highlights among those fixes:</p>
<ul>
<li>the classloader related problems with <code>@Grab</code></li>
<li>several generics handling issues</li>
<li>a problem preventing Groovy from loading its modules on IBM mainframes</li>
<li>a problem preventing Groovy from being loaded as an OSGi module in certain OSGi containers</li>
</ul>
<p>You can read the <a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=20366">JIRA release notes</a>, and <a href="http://groovy.codehaus.org/Download">download</a> the latest distribution.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy presentations at JAX</title><link>https://glaforge.dev/talks/2014/05/16/groovy-presentations-at-jax/</link><pubDate>Fri, 16 May 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/talks/2014/05/16/groovy-presentations-at-jax/</guid><description>&lt;p>I had the pleasure of coming back to the JAX conference after a number of years of absence, and I presented about what makes Groovy groovy, and functional Groovy, showing the functional aspects of the language.&lt;/p>
&lt;p>Here&amp;rsquo;s the latest update of my What makes Groovy groovy presentation:&lt;/p>
&lt;script async class="speakerdeck-embed" data-id="c3de6d10be4501313e6b7a86bf9f6a20" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js">&lt;/script>
&lt;p>And the Functional Groovy presentation as well:&lt;/p>
&lt;script async class="speakerdeck-embed" data-id="fba3dc80be4601313e6a7a86bf9f6a20" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js">&lt;/script></description><content:encoded>
<![CDATA[<p>I had the pleasure of coming back to the JAX conference after a number of years of absence, and I presented about what makes Groovy groovy, and functional Groovy, showing the functional aspects of the language.</p>
<p>Here&rsquo;s the latest update of my What makes Groovy groovy presentation:</p>
<script async class="speakerdeck-embed" data-id="c3de6d10be4501313e6b7a86bf9f6a20" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<p>And the Functional Groovy presentation as well:</p>
<script async class="speakerdeck-embed" data-id="fba3dc80be4601313e6a7a86bf9f6a20" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #21</title><link>https://glaforge.dev/posts/2014/05/13/groovy-weekly-21/</link><pubDate>Tue, 13 May 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/05/13/groovy-weekly-21/</guid><description>&lt;p>Now that &lt;a href="https://glaforge.dev/posts/2014/05/05/groovy-2-3-0-is-out/">Groovy 2.3.0 is out&lt;/a>, it’s going to be Grails turn! Graeme announced a release candidate for Grails 2.4, so take your chance to test drive it to make Grails 2.4 an awesome release!&lt;/p>
&lt;p>Last but not least, you still have two weeks to buy your tickets for the &lt;a href="http://gr8conf.eu/#/">GR8Conf Europe 2014&lt;/a>!&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>Graeme Rocher announces the &lt;a href="https://twitter.com/grailsframework/status/464401639030657025">release of Grails 2.4 RC1&lt;/a> with Groovy 2.3 and Spring Framework 4.0.4&lt;/li>
&lt;li>&lt;a href="https://twitter.com/grooscript/status/463817185991729152">Version 0.5 of Grooscript&lt;/a> released with Groovy 2.3 traits support&lt;/li>
&lt;li>Martin Lippert announces the &lt;a href="https://spring.io/blog/2014/05/06/spring-tool-suite-and-groovy-grails-tool-suite-3-5-1-released">release of Groovy / Grails Tool Suite 3.5.1&lt;/a> with support for Grails 2.3.8 and Groovy 2.3.0&lt;/li>
&lt;li>&lt;a href="https://twitter.com/javabake/status/465518849748922368">JBake 2.3.0&lt;/a> is out with Groovy and Thymeleaf template support&lt;/li>
&lt;li>Benjamin Muschko releases a &lt;a href="https://github.com/bmuschko/gradle-docker-plugin">Gradle Docker plugin&lt;/a> using the remote API for managing images and containers&lt;/li>
&lt;li>&lt;a href="https://twitter.com/AndreyHihlovski/status/465957295076442112">Gretty 0.0.16&lt;/a> is out, the advanced Gradle plugin for running webapps on Jetty&lt;/li>
&lt;/ul>
&lt;h2 id="interviews">Interviews&lt;/h2>
&lt;ul>
&lt;li>InfoWorld&amp;rsquo;s Paul Krill interviewed &lt;a href="http://www.infoworld.com/t/java-programming/one-now-two-later-groovy-updates-add-java-8-support-242229">Guillaume Laforge about the release of Groovy&lt;/a> 2.3.0&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>Adoption of the (groovy-based) &lt;a href="https://www.youtube.com/watch?v=FeSKrBvT72c">Job DSL plugin&lt;/a> (for Jenkins) at Netflix&lt;/li>
&lt;li>Second part of the &lt;a href="http://www.gradleware.com/news/blog/video-tutorial-part-2-gradle-workshop-peter-niederwieser">Gradle Workshop&lt;/a> by Peter Niederwieser from the Gradle Summit 2013&lt;/li>
&lt;li>Trisha Gee demoes &lt;a href="http://www.infoq.com/presentations/demo-java-javascript-mongodb">building a web application using Java, HTML5, Angular.js, Mongo.DB, Groovy and microservices&lt;/a> in one hour.&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>Announcement of the &lt;a href="https://spring.io/blog/2014/05/07/groovy-2-3-released">Groovy 2.3.0 release on the Spring.IO blog&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://blogs.steeplesoft.com/posts/2013/09/10/gradle-tip-attaching-a-debugger/">Gradle tip for how to attach a debugger&lt;/a>&lt;/li>
&lt;li>An article on &lt;a href="http://java.dzone.com/articles/groovy-closures-owner-delegate">Groovy DSLs with the help of closures and delegates&lt;/a> by Alex Staveley&lt;/li>
&lt;li>&lt;a href="http://www.kubrynski.com/2014/01/spring-integration-testing-with-spock.html">Spring integration testing with Spock&lt;/a> by Jakub Kubrynski&lt;/li>
&lt;li>An update on the &lt;a href="http://codewader.blogspot.no/2014/05/a-groovy-light-table-client-step-3.html">Groovy Light Table client&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau shows an example of &lt;a href="https://gist.github.com/melix/240e961b6cce70d37592">improved composition using fragments and the Markup Template Engine&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>Video of the &lt;a href="https://twitter.com/pledbrook/status/464403159524601856">Groovy Podcast episode 1&lt;/a> by Peter Ledbrook&lt;/li>
&lt;li>Peter Ledbrook also published the &lt;a href="https://twitter.com/pledbrook/status/464413601009508353">audio of the Groovy podcast&lt;/a>&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/autentia/status/463928823168253953">interviews recorded at Greach 2014&lt;/a> are now subtitled&lt;/li>
&lt;li>Rob Fletcher calls to action to &lt;a href="https://twitter.com/rfletcherew/status/466176067557130242">vote for the traits support in IntelliJ IDEA&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="mailing-lists-discussions">Mailing-lists discussions&lt;/h2>
&lt;ul>
&lt;li>The &lt;a href="http://grails.1312388.n4.nabble.com/Deprecating-The-Grails-Mailing-Lists-td4656735.html">Grails mailing-lists from Codehaus are closing&lt;/a>, announced Jeff Brown. Technical questions will be headed to StackOverflow, whereas a dedicated Google Group is being created for general discussions and announcements&lt;/li>
&lt;/ul>
&lt;h2 id="google-posts">Google+ Posts&lt;/h2>
&lt;ul>
&lt;li>Ryan Vanderwerf shares a little &lt;a href="https://plus.google.com/b/101432359761228268146/118206038047009918474/posts/aGHTV1MNLBG?cfem=1">preview of the &amp;ldquo;Getting Groovy with Lego Mindstorms EV3&amp;rdquo; workshop&lt;/a> at the upcoming GR8Conf Europe 2014&lt;/li>
&lt;/ul>
&lt;h2 id="news">News&lt;/h2>
&lt;ul>
&lt;li>You can try GrooScript online to &lt;a href="https://twitter.com/grooscript/status/465188785589092352">convert your Groovy code to JavaScript&lt;/a>&lt;/li>
&lt;li>Robert Fisher proposes the &lt;a href="https://github.com/RobertFischer/Gradle-License-Report/blob/master/README.md">Gradle License plugin&lt;/a> to generate a report of the licenses of the dependencies used in your project&lt;/li>
&lt;/ul>
&lt;h2 id="jobs">Jobs&lt;/h2>
&lt;ul>
&lt;li>A startup in Paris, France, is &lt;a href="https://remixjobs.com/emploi/Developpement/Start-up-Developpeur-Java-Grails-passionne-H-F/24451">looking for a Grails developer&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>The SpringOne2GX 2014 conference in Dallas has published most of its &lt;a href="https://2014.event.springone2gx.com/schedule/2014-09-09">agenda&lt;/a>&lt;/li>
&lt;li>Two weeks left to &lt;a href="https://twitter.com/gr8conf/status/466104656427745280">get your GR8Conf Europe ticket&lt;/a>!&lt;/li>
&lt;li>Meet the &lt;a href="http://gr8conf.eu/#/about">GR8Conf Europe team&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>Now that <a href="https://glaforge.dev/posts/2014/05/05/groovy-2-3-0-is-out/">Groovy 2.3.0 is out</a>, it’s going to be Grails turn! Graeme announced a release candidate for Grails 2.4, so take your chance to test drive it to make Grails 2.4 an awesome release!</p>
<p>Last but not least, you still have two weeks to buy your tickets for the <a href="http://gr8conf.eu/#/">GR8Conf Europe 2014</a>!</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Graeme Rocher announces the <a href="https://twitter.com/grailsframework/status/464401639030657025">release of Grails 2.4 RC1</a> with Groovy 2.3 and Spring Framework 4.0.4</li>
<li><a href="https://twitter.com/grooscript/status/463817185991729152">Version 0.5 of Grooscript</a> released with Groovy 2.3 traits support</li>
<li>Martin Lippert announces the <a href="https://spring.io/blog/2014/05/06/spring-tool-suite-and-groovy-grails-tool-suite-3-5-1-released">release of Groovy / Grails Tool Suite 3.5.1</a> with support for Grails 2.3.8 and Groovy 2.3.0</li>
<li><a href="https://twitter.com/javabake/status/465518849748922368">JBake 2.3.0</a> is out with Groovy and Thymeleaf template support</li>
<li>Benjamin Muschko releases a <a href="https://github.com/bmuschko/gradle-docker-plugin">Gradle Docker plugin</a> using the remote API for managing images and containers</li>
<li><a href="https://twitter.com/AndreyHihlovski/status/465957295076442112">Gretty 0.0.16</a> is out, the advanced Gradle plugin for running webapps on Jetty</li>
</ul>
<h2 id="interviews">Interviews</h2>
<ul>
<li>InfoWorld&rsquo;s Paul Krill interviewed <a href="http://www.infoworld.com/t/java-programming/one-now-two-later-groovy-updates-add-java-8-support-242229">Guillaume Laforge about the release of Groovy</a> 2.3.0</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Adoption of the (groovy-based) <a href="https://www.youtube.com/watch?v=FeSKrBvT72c">Job DSL plugin</a> (for Jenkins) at Netflix</li>
<li>Second part of the <a href="http://www.gradleware.com/news/blog/video-tutorial-part-2-gradle-workshop-peter-niederwieser">Gradle Workshop</a> by Peter Niederwieser from the Gradle Summit 2013</li>
<li>Trisha Gee demoes <a href="http://www.infoq.com/presentations/demo-java-javascript-mongodb">building a web application using Java, HTML5, Angular.js, Mongo.DB, Groovy and microservices</a> in one hour.</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Announcement of the <a href="https://spring.io/blog/2014/05/07/groovy-2-3-released">Groovy 2.3.0 release on the Spring.IO blog</a></li>
<li><a href="http://blogs.steeplesoft.com/posts/2013/09/10/gradle-tip-attaching-a-debugger/">Gradle tip for how to attach a debugger</a></li>
<li>An article on <a href="http://java.dzone.com/articles/groovy-closures-owner-delegate">Groovy DSLs with the help of closures and delegates</a> by Alex Staveley</li>
<li><a href="http://www.kubrynski.com/2014/01/spring-integration-testing-with-spock.html">Spring integration testing with Spock</a> by Jakub Kubrynski</li>
<li>An update on the <a href="http://codewader.blogspot.no/2014/05/a-groovy-light-table-client-step-3.html">Groovy Light Table client</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Cédric Champeau shows an example of <a href="https://gist.github.com/melix/240e961b6cce70d37592">improved composition using fragments and the Markup Template Engine</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Video of the <a href="https://twitter.com/pledbrook/status/464403159524601856">Groovy Podcast episode 1</a> by Peter Ledbrook</li>
<li>Peter Ledbrook also published the <a href="https://twitter.com/pledbrook/status/464413601009508353">audio of the Groovy podcast</a></li>
<li>The <a href="https://twitter.com/autentia/status/463928823168253953">interviews recorded at Greach 2014</a> are now subtitled</li>
<li>Rob Fletcher calls to action to <a href="https://twitter.com/rfletcherew/status/466176067557130242">vote for the traits support in IntelliJ IDEA</a></li>
</ul>
<h2 id="mailing-lists-discussions">Mailing-lists discussions</h2>
<ul>
<li>The <a href="http://grails.1312388.n4.nabble.com/Deprecating-The-Grails-Mailing-Lists-td4656735.html">Grails mailing-lists from Codehaus are closing</a>, announced Jeff Brown. Technical questions will be headed to StackOverflow, whereas a dedicated Google Group is being created for general discussions and announcements</li>
</ul>
<h2 id="google-posts">Google+ Posts</h2>
<ul>
<li>Ryan Vanderwerf shares a little <a href="https://plus.google.com/b/101432359761228268146/118206038047009918474/posts/aGHTV1MNLBG?cfem=1">preview of the &ldquo;Getting Groovy with Lego Mindstorms EV3&rdquo; workshop</a> at the upcoming GR8Conf Europe 2014</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>You can try GrooScript online to <a href="https://twitter.com/grooscript/status/465188785589092352">convert your Groovy code to JavaScript</a></li>
<li>Robert Fisher proposes the <a href="https://github.com/RobertFischer/Gradle-License-Report/blob/master/README.md">Gradle License plugin</a> to generate a report of the licenses of the dependencies used in your project</li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li>A startup in Paris, France, is <a href="https://remixjobs.com/emploi/Developpement/Start-up-Developpeur-Java-Grails-passionne-H-F/24451">looking for a Grails developer</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>The SpringOne2GX 2014 conference in Dallas has published most of its <a href="https://2014.event.springone2gx.com/schedule/2014-09-09">agenda</a></li>
<li>Two weeks left to <a href="https://twitter.com/gr8conf/status/466104656427745280">get your GR8Conf Europe ticket</a>!</li>
<li>Meet the <a href="http://gr8conf.eu/#/about">GR8Conf Europe team</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #20</title><link>https://glaforge.dev/posts/2014/05/06/groovy-weekly-20/</link><pubDate>Tue, 06 May 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/05/06/groovy-weekly-20/</guid><description>&lt;p>Tthe big news of the week is the &lt;a href="https://glaforge.dev/posts/2014/05/05/groovy-2-3-0-is-out/">release of Groovy 2.3.0&lt;/a>! With traits, support for running on JDK 8, new AST transformations, lightning fast JSON support, and more!&lt;/p>
&lt;p>Be sure to read the Groovy 2.3.0 &lt;a href="http://bitly.com/g230notes">release notes&lt;/a> to get all the juicy details!&lt;/p>
&lt;p>And vote up the DZone link to make a bit of buzz on the small &lt;a href="http://www.dzone.com/links/interview_about_the_release_of_groovy_23.html">interview of Guillaume Laforge&lt;/a> about the release.&lt;/p>
&lt;p>Also, Hubert Klein Ikking was on a blogging spree, as he wrote many new items in the Groovy Goodness series with snippets showing the new features in action. So don’t miss those examples linked below!&lt;/p></description><content:encoded>
<![CDATA[<p>Tthe big news of the week is the <a href="https://glaforge.dev/posts/2014/05/05/groovy-2-3-0-is-out/">release of Groovy 2.3.0</a>! With traits, support for running on JDK 8, new AST transformations, lightning fast JSON support, and more!</p>
<p>Be sure to read the Groovy 2.3.0 <a href="http://bitly.com/g230notes">release notes</a> to get all the juicy details!</p>
<p>And vote up the DZone link to make a bit of buzz on the small <a href="http://www.dzone.com/links/interview_about_the_release_of_groovy_23.html">interview of Guillaume Laforge</a> about the release.</p>
<p>Also, Hubert Klein Ikking was on a blogging spree, as he wrote many new items in the Groovy Goodness series with snippets showing the new features in action. So don’t miss those examples linked below!</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Guillaume Laforge announces the <a href="https://glaforge.dev/posts/2014/05/05/groovy-2-3-0-is-out/">final release of Groovy 2.3.0</a></li>
<li><a href="http://www.ratpack.io/versions/0.9.4">Ratpack 0.9.4</a> relased</li>
<li>Schalk Cronjé shares a pre-release of his <a href="http://groovy.329449.n5.nabble.com/ANN-VFS-Command-line-pre-release-version-on-Bintray-td5719457.html">Groovy VFS command-line project</a>, for copying, moving files around from multiple sources from the command-line</li>
<li>Florian Freudenberg created a <a href="https://github.com/flofreud/posh-gvm">Windows Powershell port of GVM command-line</a> interface for Groovy developers using Windows, thanks to the GVM APIs</li>
<li>Latitude 1.0 has been released. <a href="https://github.com/will-lp/latitude">Latitude is an API to ease property reference in Groovy</a>, allowing it to be type checked and customized for use within queries, ignoring fields, etc. An IDE can auto-complete, refactor and find usages, and @TypeChecked can point typos in it.</li>
</ul>
<h2 id="interviews">Interviews</h2>
<ul>
<li>InfoQ publishes a little <a href="http://www.dzone.com/links/interview_about_the_release_of_groovy_23.html">interview of Guillaume Laforge about the release of Groovy 2.3.0</a>. You can vote up the associated DZone link!</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Vladimír Orany spoke about <a href="http://greach.es/speakers/vladimir-orany-gliding-gaelyk/">Gaelyk and Glide</a> at Greach 2014</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Learn about all the latest <a href="http://bitly.com/g230notes">features of Groovy 2.3.0 in the detailed release notes</a></li>
<li>InfoQ features an article by Matt Raible on the <a href="http://www.infoq.com/news/2014/04/groovy-2.3-json">JSON performance improvements</a> coming up in Groovy 2.3</li>
<li><a href="http://blogs.msdn.com/b/jerrynixon/archive/2014/02/26/at-last-c-is-getting-sometimes-called-the-safe-navigation-operator.aspx">C# is getting Groovy</a>, borrowing Groovy&rsquo;s ?. null safe navigation operator (already copied by CoffeeScript)</li>
<li>David Norton writes about how to <a href="http://www.objectpartners.com/2014/04/24/reuse-your-gradle-logic-across-the-enterprise/">reuse your Gradle logic across the enterprise</a></li>
<li>MrHaki&rsquo;s Groovy Goodness celebrates Groovy 2.3.0!</li>
<li><a href="http://mrhaki.blogspot.dk/2014/05/groovy-goodness-use-sortable-annotation.html">use @Sortable annotation to make classes Comparable</a></li>
<li><a href="http://mrhaki.blogspot.fr/2014/05/groovy-goodness-extra-methods-for-nio.html">extra methods for NIO path</a></li>
<li><a href="http://mrhaki.blogspot.fr/2014/05/groovy-goodness-more-efficient-tail.html">more efficient tail recursion with @TailRecursive annotation</a></li>
<li><a href="http://mrhaki.blogspot.fr/2014/05/groovy-goodness-basescript-with.html">@BaseScript with abstract run script method</a></li>
<li><a href="http://mrhaki.blogspot.fr/2014/05/groovy-goodness-use-builder-ast.html">use @Builder AST transformation for fluent API</a></li>
<li><a href="http://mrhaki.blogspot.fr/2014/05/groovy-goodness-builder-definition-with.html">@Builder definition with extra type checks</a></li>
<li><a href="http://mrhaki.blogspot.fr/2014/05/groovy-goodness-using-builder-to-create.html">using @Builder to create fluent API for other classes</a></li>
<li><a href="http://mrhaki.blogspot.fr/2014/05/groovy-goodness-check-configuration.html">check configuration property is set in ConfigObject</a></li>
<li><a href="http://mrhaki.blogspot.fr/2014/05/groovy-goodness-extend-configslurper.html">extend ConfigSlurper with custom environments</a> sections</li>
<li>Grails Goodness by MrHaki: <a href="http://mrhaki.blogspot.fr/2014/05/grails-goodness-using-aliases-as.html">Using Aliases as Command Shortcuts</a></li>
<li><a href="http://www.gobloomhealth.com/multi-factor-authentication-part-1/">Multi-factor authentication</a> with Grails, part 1, by Kyle Boon</li>
<li><a href="http://deveki.com/softdev/gorm-bulk-delete-already/">GORM: bulk delete already</a></li>
<li><a href="http://octodecillion.com/blog/getting-svn-info-using-groovy/">Getting SVN info using Groovy</a></li>
<li>Implementing <a href="http://www.oodlestechnologies.com/blogs/Implementing-many-to-many-relation-between-domains-in-Grails-with-MongoDB">many to many relation between domains in Grails with MongoDB</a></li>
<li><a href="http://www.objectpartners.com/2014/04/22/tracking-hibernate-statistics-across-grails-actions/">Tracking Hibernate statistics across Grails actions</a> by Igor Shults</li>
<li><a href="http://octodecillion.com/blog/groovy-implementation-of-inix-file-format-part-2/">Groovy implementation of the INI</a> file format</li>
<li><a href="http://www.oodlestechnologies.com/blogs/Implement-SocialAuth-using-Grails">Implementing social auth with Grails</a> by Sumit Rathi</li>
<li><a href="http://www.intelligrape.com/blog/2014/04/28/spring-security-permission-based-framework/">Spring Security Permission Based framework</a></li>
<li><a href="http://www.intelligrape.com/blog/2014/04/22/simple-debugging-in-intellij-idea-part-1/">Debugging your Grails application in IntelliJ IDEA</a></li>
<li><a href="http://www.intelligrape.com/blog/2014/04/28/facebook-oauth-for-fetching-page-token/">Facebook Oauth</a> for fetching page token with Grails</li>
<li><a href="http://groovy.dzone.com/articles/groovy-database-resource">Groovy database resource handling</a> by Robert Greathouse</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>The link to <a href="https://twitter.com/DZoneLinks/status/463679195147538432">Guillaume Laforge&rsquo;s interview on DZone is promoted as a &ldquo;big link&rdquo;</a>!</li>
<li><a href="https://twitter.com/gvmtool/status/463356426354954240">Groovy 2.3.0</a> is available through GVM</li>
<li>Ken Kousen reports the <a href="https://twitter.com/kenkousen/status/463656597068058624">change in the groovysh commands now starting with colons</a></li>
<li>Peter Ledbrook announces the <a href="https://twitter.com/pledbrook/status/463251293394710529">next Groovy podcast</a> for Thursday</li>
<li>Jörn Huxhorn noticed the <a href="https://twitter.com/huxi/status/463372504518909952">reworked generics handling of Groovy 2.3 found bugs that slipped through the old type checker</a>! But if you find regressions in this area, please report them!</li>
<li>Sébastien Blanc visited the <a href="https://twitter.com/sebi2706/status/461064801951952896">Fort Griffon in Besançon, France</a>. In honor of Andrés Almiray&rsquo;s work on the Griffon framework?</li>
<li>Bertrand Goetzmann shares some <a href="https://twitter.com/bgoetzmann/status/461468578387349504">Groovy examples for scripting his TiddlyWikiFx</a> project</li>
<li>Peter Ledbrook reminds us with his funny tweet that it&rsquo;s better to <a href="https://twitter.com/pledbrook/status/461838863900893184">use the #groovylang hashtag on Twitter</a> rather than #groovy as there&rsquo;s less noise about non-Groovy language related topics</li>
</ul>
<h2 id="google-posts">Google+ Posts</h2>
<ul>
<li>Xavier Ducrohet announces a <a href="https://plus.google.com/u/0/+AndroidDevelopers/posts/QCpXF1BxKPm">new version of the Gradle Android plugin</a></li>
</ul>
<h2 id="code-snippets">Code Snippets</h2>
<ul>
<li>Tim Yates pushes a gist with variants of a <a href="https://gist.github.com/glaforge/397fc259320ffd0f7c25">gaussian distribution with Java 8, Groovy, and GPars</a></li>
<li>Tim Yates plays with a <a href="https://gist.github.com/timyates/527e116c225b23c89e16">metaprogramming addition to Java 8&rsquo;s LocalDate</a> to support Groovy&rsquo;s + days addition.</li>
<li>The new <a href="https://gist.github.com/dmahapatro/5e35e59cad2736a6723f">@Builder transformation</a> of Groovy 2.3 covered with its different implementation strategies</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Jacob Aae Mikkelsen posted <a href="http://grydeske.net/news/show/42">Grails Diary</a> week 17</li>
<li>Jacob Aae Mikkelsen published week 18 of the <a href="http://grydeske.net/news/show/43">Grails Diary</a></li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li><a href="https://twitter.com/manningmeap/status/461907899540074496">Grails in Action, Second Edition</a>, by Peter Ledbrook and Glen Smith, has updated all chapters &amp; appendices</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>With all the <a href="https://twitter.com/greach_es/status/461414161940226048">videos published from the Greach</a> 2014 conference, viewers accumulated 73000 minutes of viewing!</li>
<li>GR8Conf US 2014 <a href="https://twitter.com/ddelponte/status/461225051527069697">registration</a> is open</li>
<li>GR8Conf Europe announces <a href="https://twitter.com/gr8conf/status/463524508176220160">Cygnet Infotech as sponsor</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.3.0 is out!</title><link>https://glaforge.dev/posts/2014/05/05/groovy-2-3-0-is-out/</link><pubDate>Mon, 05 May 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/05/05/groovy-2-3-0-is-out/</guid><description>&lt;p>The Groovy development team is proud to announce the final release of Groovy 2.3.0!&lt;/p>
&lt;p>Groovy 2.3 is the new major release of Groovy, featuring official support for running Groovy on JDK 8, traits, new and improved AST transformations like &lt;code>@TailRecursive&lt;/code>, &lt;code>@Builder&lt;/code> and &lt;code>@Sortable&lt;/code>, a new NIO2 module with Path support, lightening fast JSON parsing and building, closure parameter type inference, a new markup template engine, Groovysh and GroovyConsole ease of use improvements, a new &lt;code>GroovyAssert&lt;/code> test utility, more &lt;code>@BaseScript&lt;/code> class capabilities, and more.&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy development team is proud to announce the final release of Groovy 2.3.0!</p>
<p>Groovy 2.3 is the new major release of Groovy, featuring official support for running Groovy on JDK 8, traits, new and improved AST transformations like <code>@TailRecursive</code>, <code>@Builder</code> and <code>@Sortable</code>, a new NIO2 module with Path support, lightening fast JSON parsing and building, closure parameter type inference, a new markup template engine, Groovysh and GroovyConsole ease of use improvements, a new <code>GroovyAssert</code> test utility, more <code>@BaseScript</code> class capabilities, and more.</p>
<p>Please read the <a href="http://bit.ly/g230notes">Groovy 2.3.0 release notes</a> for further details on all these exciting new features!</p>
<p>You can <a href="http://groovy.codehaus.org/Download">download Groovy</a> at the usual location.</p>
<p>JIRA will give you the <a href="http://bit.ly/g230jira">detailed change log</a> for this release.</p>
<p>Groovy wouldn’t be what it is without your support, your bug reports and feature requests, your contributions and pull requests, so big thanks to all those who contributed to this release in a way or another, including the usual suspects of the core team (Jochen, Paul, Cédric and myself), but also André, Andres, Tim, Pascal, Johanes, Paolo, Thibault, Rick and Andrey, Jim (base script class), Damien, Kenneth and several others I’ve forgotten to mention!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.3.0 RC 4 is out</title><link>https://glaforge.dev/posts/2014/05/01/groovy-2-3-0-rc-4-is-out/</link><pubDate>Thu, 01 May 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/05/01/groovy-2-3-0-rc-4-is-out/</guid><description>&lt;p>To celebrate the &lt;a href="http://en.wikipedia.org/wiki/International_Workers'_Day">International Workers&amp;rsquo; Day&lt;/a> (a holiday in half of the world&amp;rsquo;s countries, no less! including mine), we&amp;rsquo;re announcing the release of Groovy 2.3.0-RC-4.&lt;/p>
&lt;p>Yes, RC-4 and not RC-3, as we&amp;rsquo;re still having some issues with our build and deployment infrastructure which made us have to skip a bit.&lt;/p>
&lt;p>Thanks to your feedback, we managed to iron out some more issues and regressions we were having with the tricky generics handling.&lt;/p></description><content:encoded>
<![CDATA[<p>To celebrate the <a href="http://en.wikipedia.org/wiki/International_Workers'_Day">International Workers&rsquo; Day</a> (a holiday in half of the world&rsquo;s countries, no less! including mine), we&rsquo;re announcing the release of Groovy 2.3.0-RC-4.</p>
<p>Yes, RC-4 and not RC-3, as we&rsquo;re still having some issues with our build and deployment infrastructure which made us have to skip a bit.</p>
<p>Thanks to your feedback, we managed to iron out some more issues and regressions we were having with the tricky generics handling.</p>
<p>You can have a look at the <a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=20359">detailed JIRA release notes</a> for the issues solved, and <a href="http://groovy.codehaus.org/Download">download the latest RC</a> build at the usual place (or just update the Gradle or Maven dependency coordinates).</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Machine Learning à Mix-IT 2014</title><link>https://glaforge.dev/talks/2014/04/30/machine-learning-a-mix-it-2014/</link><pubDate>Wed, 30 Apr 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/talks/2014/04/30/machine-learning-a-mix-it-2014/</guid><description>&lt;p>J&amp;rsquo;ai eu le plaisir de préparer et de présenter une conférence de 50 minutes sur le Machine Learning, avec mon ami &lt;a href="https://plus.google.com/+DidierGirard/posts">Didier Girard&lt;/a>, lors de la &lt;a href="http://www.mix-it.fr/">conférence Mix-IT 2014&lt;/a> à Lyon.&lt;/p>
&lt;p>50 minutes, c&amp;rsquo;est court pour présenter un si vaste et passionnant sujet !&lt;/p>
&lt;p>J&amp;rsquo;avais fait un peu de machine learning pendant mes études, mais je n&amp;rsquo;avais jamais eu l&amp;rsquo;occasion de m&amp;rsquo;y replonger dedans depuis. Mais aujourd&amp;rsquo;hui, avec de plus en plus de données récoltées (le &amp;ldquo;big data&amp;rdquo;, l&amp;rsquo; &amp;ldquo;Internet of Things&amp;rdquo;&amp;hellip;), il faut savoir apprendre de ces données, découvrir de nouvelles tendances, prévoir un comportement, classifier ces données&amp;hellip; et le Machine Learning est ce qui vous permet de faire tout ça !&lt;/p></description><content:encoded>
<![CDATA[<p>J&rsquo;ai eu le plaisir de préparer et de présenter une conférence de 50 minutes sur le Machine Learning, avec mon ami <a href="https://plus.google.com/+DidierGirard/posts">Didier Girard</a>, lors de la <a href="http://www.mix-it.fr/">conférence Mix-IT 2014</a> à Lyon.</p>
<p>50 minutes, c&rsquo;est court pour présenter un si vaste et passionnant sujet !</p>
<p>J&rsquo;avais fait un peu de machine learning pendant mes études, mais je n&rsquo;avais jamais eu l&rsquo;occasion de m&rsquo;y replonger dedans depuis. Mais aujourd&rsquo;hui, avec de plus en plus de données récoltées (le &ldquo;big data&rdquo;, l&rsquo; &ldquo;Internet of Things&rdquo;&hellip;), il faut savoir apprendre de ces données, découvrir de nouvelles tendances, prévoir un comportement, classifier ces données&hellip; et le Machine Learning est ce qui vous permet de faire tout ça !</p>
<p>Donc un sujet très porteur et très prometteur en ce moment !</p>
<p>La question nous avait été posée de savoir comment on pouvait en apprendre plus sur le Machine Learning, et pour y répondre également ici sur ce blog, je suggère à ceux qui sont intéressés de suivre un cours en ligne comme celui de <a href="https://www.coursera.org/course/machlearning">Andrew Ng de Stanford sur Coursera</a> : une approche super didactique et passionnante.</p>
<p>En attendant, voici les slides de la présentation :</p>
<script async class="speakerdeck-embed" data-id="5b7c368530274850b1ca5c7dad56cabf" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #19</title><link>https://glaforge.dev/posts/2014/04/29/groovy-weekly-19/</link><pubDate>Tue, 29 Apr 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/04/29/groovy-weekly-19/</guid><description>&lt;p>Another busy week for the Groovy ecosystem, with key releases of Groovy, Grails, Gradle, GPars, and CodeNarc! This can be labeled as a “release week”!&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>Guillaume Laforge announces the &lt;a href="https://glaforge.dev/posts/2014/04/24/release-candidate-for-groovy-2-3/">release candidate of Groovy 2.3&lt;/a>&lt;/li>
&lt;li>Guillaume Laforge announces the &lt;a href="https://glaforge.dev/posts/2014/04/28/second-release-candidate-of-groovy-2-3/">second release candidate of Groovy 2.3&lt;/a>&lt;/li>
&lt;li>Graeme Rocher announces a Grails joint release with &lt;a href="http://grails.1312388.n4.nabble.com/ANN-Grails-2-3-8-amp-2-4-0-M1-out-td4656282.html">Grails 2.3.8 and Grails 2.4-milestone-2&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://forums.gradle.org/gradle/topics/gradle_1_12_released">Gradle 1.12 released&lt;/a> with some nice universal performance improvements, more control of the dependency resolution process, an improved API for IDE integration, Clang and CUnit support for native code and a slew of bug and compatibility fixes&lt;/li>
&lt;li>&lt;a href="https://twitter.com/grailsframework/status/459597120581144576">Release of GORM for MongoDB v3.0&lt;/a> with MongoDB 2.6 support (new GeoJSON types, text search)&lt;/li>
&lt;li>The CodeNarc static code analysis tool for Groovy releases &lt;a href="http://groovy.329449.n5.nabble.com/ANN-Announcing-CodeNarc-0-21-td5719330.html">CodeNarc version 0.21, with 15 new rules&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://gpars-user-mailing-list.19372.n3.nabble.com/GPars-1-2-0-released-td4024974.html">GPars 1.2&lt;/a> has been released&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>Jorge Franco presents &lt;a href="http://greach.es/speakers/jorge-franco-grooscript/">GrooScript, the Groovy to JavaScript compiler&lt;/a>, at Greach 2014&lt;/li>
&lt;li>Cédric Champeau gave an excellent workshop at Greach 2014 entitled &amp;ldquo;&lt;a href="http://greach.es/speakers/cedric-champeau-unleashing-the-power-of-ast-transformations/">unleashing the power of AST transformations&lt;/a>&amp;rdquo;&lt;/li>
&lt;li>Cédric Champeau and Fabrice Matrat present about &amp;ldquo;Groovy, head in the cloud&amp;rdquo;, at Greach 2014, where they show &lt;a href="http://greach.es/speakers/fabrice-matrat-cedric-champeau-groovy-head-in-the-cloud/">how to secure Groovy code in a cloud and shared environment&lt;/a>&lt;/li>
&lt;li>Rick Hightower created a presentation on &lt;a href="http://fr.slideshare.net/richardhightower/java-json-benchmakr">Java JSON parser benchmarking&lt;/a>, upon his work on the Groovy and Boon parsers performance work&lt;/li>
&lt;li>&lt;a href="http://greach.es/speakers/fernando-redondo-can-you-tell-me-how-to-get-to-sesame-street-i-wanna-be-a-grails-rookie-star-there/">Grails workshop&lt;/a> given at Greach 2014 by Fernando Redondon&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>MrHaki writes about &lt;a href="http://mrhaki.blogspot.fr/2014/04/groovy-goodness-restricting-script.html">restricting script syntax with SecureASTCustomizer&lt;/a>&lt;/li>
&lt;li>MrHaki defines &lt;a href="http://mrhaki.blogspot.fr/2014/04/groovy-goodness-define-compilation.html">compilation customizers with the builder syntax&lt;/a>&lt;/li>
&lt;li>MrHaki explains how to &lt;a href="http://mrhaki.blogspot.fr/2014/04/groovy-goodness-customize-tostring.html">customize the @ToString transform&lt;/a>&lt;/li>
&lt;li>Using &lt;a href="http://wordpress.transentia.com.au/wordpress/2014/04/23/using-spock-configuration-in-grails/">Spock configuration with Grails&lt;/a>&lt;/li>
&lt;li>Julien Ponge comes back on Andrey Bloschetsov and Rick Hightower &lt;a href="http://julien.ponge.org/blog/revisiting-a-json-benchmark/">JSON benchmarks&lt;/a>, with a few updates to improve the reliability of the benchmarks. Boon is underlying the new JSON capabilities of Groovy 2.3 while the Groovy represented in this article refers to the pre-Groovy 2.3 parser and serializer.&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau is teasing about his work on &lt;a href="https://twitter.com/CedricChampeau/status/459260216841097216">Groovy support for Android&lt;/a>&lt;/li>
&lt;li>Graeme Rocher shows the compelling &lt;a href="https://twitter.com/graemerocher/status/461065311819554817">conciseness and readability of GORM vs raw Hibernate&lt;/a>&lt;/li>
&lt;li>The GR8Conf Europe team is &lt;a href="https://twitter.com/gr8conf/status/460508009522003968">looking for a name for its last special conference beer brew&lt;/a>, if you’ve got suggestions&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/459428432557129728">Groovy 2.3-rc-1&lt;/a> available through GVM&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/459376588539576320">Gradle 1.12-rc-2&lt;/a> is available through GVM&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/459366821783928833">Grails 2.3.8 and 2.4.0-milestone-2&lt;/a> now available for download on GVM&lt;/li>
&lt;li>Dan Allen mentions that a Google Summer of Code student will be working on a &lt;a href="https://twitter.com/mojavelinux/status/459500064604712961">Groovy scripting frontend for Arquillian&lt;/a>&lt;/li>
&lt;li>Peter Ledbrook, scratching his head, points at the &lt;a href="https://twitter.com/pledbrook/status/459440167263408128">first Grails podcast episode in video&lt;/a> form&lt;/li>
&lt;li>Peter Ledbrook published an &lt;a href="https://twitter.com/pledbrook/status/461034693279121408">MP3 version of the Grails podcast&lt;/a> in addition to the video stream&lt;/li>
&lt;li>Alex Sanchez remarks that lots of &lt;a href="https://twitter.com/AlexSanchezSTH/status/459353715506708480">Java developers are getting acquainted with Groovy thanks to Gradle and Spock&lt;/a>&lt;/li>
&lt;li>Bertrand Goetzmann notes that &lt;a href="https://twitter.com/bgoetzmann/status/459062310691430400">Grails also has a &amp;ldquo;wrapper&amp;rdquo; like Gradle&lt;/a> to ease having everybody on the team run the very same version of Grails&lt;/li>
&lt;li>Looking at the &lt;a href="https://twitter.com/gregopet/status/459617010239143936">Groovy meta-programming documentation reminds us how awesome Groovy is&lt;/a>!&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code Snippets&lt;/h2>
&lt;ul>
&lt;li>How to &lt;a href="https://github.com/grails/grails-boot/tree/master/sample-apps/gsp">use Grails&amp;rsquo; GSP view technology in Spring Boot&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="news">News&lt;/h2>
&lt;ul>
&lt;li>Netflix is well known for its heavy use of Groovy, Grails and Gradle for their infrastructure, and they are sharing project Nebula, a &lt;a href="http://nebula-plugins.github.io/">platform for handling and sharing Gradle plugins&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/gr8conf/status/459445157017174016">Object Partners become a sponsor of GR8Conf Europe&lt;/a> 2014&lt;/li>
&lt;li>The GR8Conf Europe crew is &lt;a href="https://twitter.com/brianjohnsendk/status/460047137490075648">brewing the official conference beverage&lt;/a>&lt;/li>
&lt;li>The &lt;a href="http://gr8conf.eu/#/agenda">agenda of GR8Conf Europe 2014&lt;/a> has been posted&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>Another busy week for the Groovy ecosystem, with key releases of Groovy, Grails, Gradle, GPars, and CodeNarc! This can be labeled as a “release week”!</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Guillaume Laforge announces the <a href="https://glaforge.dev/posts/2014/04/24/release-candidate-for-groovy-2-3/">release candidate of Groovy 2.3</a></li>
<li>Guillaume Laforge announces the <a href="https://glaforge.dev/posts/2014/04/28/second-release-candidate-of-groovy-2-3/">second release candidate of Groovy 2.3</a></li>
<li>Graeme Rocher announces a Grails joint release with <a href="http://grails.1312388.n4.nabble.com/ANN-Grails-2-3-8-amp-2-4-0-M1-out-td4656282.html">Grails 2.3.8 and Grails 2.4-milestone-2</a></li>
<li><a href="http://forums.gradle.org/gradle/topics/gradle_1_12_released">Gradle 1.12 released</a> with some nice universal performance improvements, more control of the dependency resolution process, an improved API for IDE integration, Clang and CUnit support for native code and a slew of bug and compatibility fixes</li>
<li><a href="https://twitter.com/grailsframework/status/459597120581144576">Release of GORM for MongoDB v3.0</a> with MongoDB 2.6 support (new GeoJSON types, text search)</li>
<li>The CodeNarc static code analysis tool for Groovy releases <a href="http://groovy.329449.n5.nabble.com/ANN-Announcing-CodeNarc-0-21-td5719330.html">CodeNarc version 0.21, with 15 new rules</a></li>
<li><a href="http://gpars-user-mailing-list.19372.n3.nabble.com/GPars-1-2-0-released-td4024974.html">GPars 1.2</a> has been released</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Jorge Franco presents <a href="http://greach.es/speakers/jorge-franco-grooscript/">GrooScript, the Groovy to JavaScript compiler</a>, at Greach 2014</li>
<li>Cédric Champeau gave an excellent workshop at Greach 2014 entitled &ldquo;<a href="http://greach.es/speakers/cedric-champeau-unleashing-the-power-of-ast-transformations/">unleashing the power of AST transformations</a>&rdquo;</li>
<li>Cédric Champeau and Fabrice Matrat present about &ldquo;Groovy, head in the cloud&rdquo;, at Greach 2014, where they show <a href="http://greach.es/speakers/fabrice-matrat-cedric-champeau-groovy-head-in-the-cloud/">how to secure Groovy code in a cloud and shared environment</a></li>
<li>Rick Hightower created a presentation on <a href="http://fr.slideshare.net/richardhightower/java-json-benchmakr">Java JSON parser benchmarking</a>, upon his work on the Groovy and Boon parsers performance work</li>
<li><a href="http://greach.es/speakers/fernando-redondo-can-you-tell-me-how-to-get-to-sesame-street-i-wanna-be-a-grails-rookie-star-there/">Grails workshop</a> given at Greach 2014 by Fernando Redondon</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>MrHaki writes about <a href="http://mrhaki.blogspot.fr/2014/04/groovy-goodness-restricting-script.html">restricting script syntax with SecureASTCustomizer</a></li>
<li>MrHaki defines <a href="http://mrhaki.blogspot.fr/2014/04/groovy-goodness-define-compilation.html">compilation customizers with the builder syntax</a></li>
<li>MrHaki explains how to <a href="http://mrhaki.blogspot.fr/2014/04/groovy-goodness-customize-tostring.html">customize the @ToString transform</a></li>
<li>Using <a href="http://wordpress.transentia.com.au/wordpress/2014/04/23/using-spock-configuration-in-grails/">Spock configuration with Grails</a></li>
<li>Julien Ponge comes back on Andrey Bloschetsov and Rick Hightower <a href="http://julien.ponge.org/blog/revisiting-a-json-benchmark/">JSON benchmarks</a>, with a few updates to improve the reliability of the benchmarks. Boon is underlying the new JSON capabilities of Groovy 2.3 while the Groovy represented in this article refers to the pre-Groovy 2.3 parser and serializer.</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Cédric Champeau is teasing about his work on <a href="https://twitter.com/CedricChampeau/status/459260216841097216">Groovy support for Android</a></li>
<li>Graeme Rocher shows the compelling <a href="https://twitter.com/graemerocher/status/461065311819554817">conciseness and readability of GORM vs raw Hibernate</a></li>
<li>The GR8Conf Europe team is <a href="https://twitter.com/gr8conf/status/460508009522003968">looking for a name for its last special conference beer brew</a>, if you’ve got suggestions</li>
<li><a href="https://twitter.com/gvmtool/status/459428432557129728">Groovy 2.3-rc-1</a> available through GVM</li>
<li><a href="https://twitter.com/gvmtool/status/459376588539576320">Gradle 1.12-rc-2</a> is available through GVM</li>
<li><a href="https://twitter.com/gvmtool/status/459366821783928833">Grails 2.3.8 and 2.4.0-milestone-2</a> now available for download on GVM</li>
<li>Dan Allen mentions that a Google Summer of Code student will be working on a <a href="https://twitter.com/mojavelinux/status/459500064604712961">Groovy scripting frontend for Arquillian</a></li>
<li>Peter Ledbrook, scratching his head, points at the <a href="https://twitter.com/pledbrook/status/459440167263408128">first Grails podcast episode in video</a> form</li>
<li>Peter Ledbrook published an <a href="https://twitter.com/pledbrook/status/461034693279121408">MP3 version of the Grails podcast</a> in addition to the video stream</li>
<li>Alex Sanchez remarks that lots of <a href="https://twitter.com/AlexSanchezSTH/status/459353715506708480">Java developers are getting acquainted with Groovy thanks to Gradle and Spock</a></li>
<li>Bertrand Goetzmann notes that <a href="https://twitter.com/bgoetzmann/status/459062310691430400">Grails also has a &ldquo;wrapper&rdquo; like Gradle</a> to ease having everybody on the team run the very same version of Grails</li>
<li>Looking at the <a href="https://twitter.com/gregopet/status/459617010239143936">Groovy meta-programming documentation reminds us how awesome Groovy is</a>!</li>
</ul>
<h2 id="code-snippets">Code Snippets</h2>
<ul>
<li>How to <a href="https://github.com/grails/grails-boot/tree/master/sample-apps/gsp">use Grails&rsquo; GSP view technology in Spring Boot</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Netflix is well known for its heavy use of Groovy, Grails and Gradle for their infrastructure, and they are sharing project Nebula, a <a href="http://nebula-plugins.github.io/">platform for handling and sharing Gradle plugins</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="https://twitter.com/gr8conf/status/459445157017174016">Object Partners become a sponsor of GR8Conf Europe</a> 2014</li>
<li>The GR8Conf Europe crew is <a href="https://twitter.com/brianjohnsendk/status/460047137490075648">brewing the official conference beverage</a></li>
<li>The <a href="http://gr8conf.eu/#/agenda">agenda of GR8Conf Europe 2014</a> has been posted</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Second release candidate of Groovy 2.3</title><link>https://glaforge.dev/posts/2014/04/28/second-release-candidate-of-groovy-2-3/</link><pubDate>Mon, 28 Apr 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/04/28/second-release-candidate-of-groovy-2-3/</guid><description>&lt;p>The Groovy team is happy to announce the &lt;strong>release of Groovy 2.3.0-rc-2&lt;/strong>. This should be the last release candidate until the final release in a couple days.&lt;/p>
&lt;p>This release ironed out a few bugs, for instance in generics handling, or made the groovydoc tool aware of traits.&lt;/p>
&lt;p>Note that the latest fixes in the traits support introduce a binary incompatibility compared to the previous release candidate.&lt;/p>
&lt;p>You can have a look at the JIRA release notes: &lt;a href="https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;version=20357">https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;version=20357&lt;/a>&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy team is happy to announce the <strong>release of Groovy 2.3.0-rc-2</strong>. This should be the last release candidate until the final release in a couple days.</p>
<p>This release ironed out a few bugs, for instance in generics handling, or made the groovydoc tool aware of traits.</p>
<p>Note that the latest fixes in the traits support introduce a binary incompatibility compared to the previous release candidate.</p>
<p>You can have a look at the JIRA release notes: <a href="https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=20357">https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=20357</a></p>
<p>And you can download Groovy at the usual place: <a href="http://groovy.codehaus.org/Download">http://groovy.codehaus.org/Download</a></p>
<p>Thanks for your attention, and keep it Groovy!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Release candidate for Groovy 2.3</title><link>https://glaforge.dev/posts/2014/04/24/release-candidate-for-groovy-2-3/</link><pubDate>Thu, 24 Apr 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/04/24/release-candidate-for-groovy-2-3/</guid><description>&lt;p>The Groovy team is happy to announce the first release candidate of Groovy 2.3!&lt;/p>
&lt;p>The major changes between this version and the last beta include:&lt;/p>
&lt;ul>
&lt;li>a simplification of the semantics of traits, by &lt;a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-rc-1/html/documentation/core-traits.html#_differences_with_java_8_default_methods">removing &lt;code>@ForceOverride&lt;/code> and making it the default&lt;/a>&lt;/li>
&lt;li>the &lt;a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-rc-1/html/documentation/core-traits.html#_chaining_behavior">implementation of stackable traits&lt;/a> for improved composition of behavior&lt;/li>
&lt;li>a new &lt;a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-rc-1/html/documentation/core-metaprogramming.html#_available_ast_transformations">&lt;code>@Builder&lt;/code> AST transformation to generate builders for immutable classes&lt;/a>&lt;/li>
&lt;li>bug fixes on generics type inference&lt;/li>
&lt;/ul>
&lt;p>For more details on the changes in this version, please refer to the &lt;a href="https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;version=20227">JIRA release notes&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy team is happy to announce the first release candidate of Groovy 2.3!</p>
<p>The major changes between this version and the last beta include:</p>
<ul>
<li>a simplification of the semantics of traits, by <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-rc-1/html/documentation/core-traits.html#_differences_with_java_8_default_methods">removing <code>@ForceOverride</code> and making it the default</a></li>
<li>the <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-rc-1/html/documentation/core-traits.html#_chaining_behavior">implementation of stackable traits</a> for improved composition of behavior</li>
<li>a new <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-rc-1/html/documentation/core-metaprogramming.html#_available_ast_transformations"><code>@Builder</code> AST transformation to generate builders for immutable classes</a></li>
<li>bug fixes on generics type inference</li>
</ul>
<p>For more details on the changes in this version, please refer to the <a href="https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=20227">JIRA release notes</a>.</p>
<p>The release can be <a href="http://groovy.codehaus.org/Download">downloaded</a> from the usual place.</p>
<p>We&rsquo;re looking forward to hearing about your feedback, and unless anything super critical arises, we&rsquo;re targeting an imminent final release next week!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #18</title><link>https://glaforge.dev/posts/2014/04/22/groovy-weekly-18/</link><pubDate>Tue, 22 Apr 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/04/22/groovy-weekly-18/</guid><description>&lt;p>The Easter bunny is delivering the latest Groovy Weekly column!&lt;/p>
&lt;p>With your chocolate eggs, you’ll get some bits of functional programming, a tutorial on Groovy traits, details of the JSON serialization performance improvements, and also get the chance of voicing your feedback on things like Groovy’s support of Java 8 lambda syntax, on the Ratpack async support, and more.&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>groovy-comprehension is a groovy extension module / AST transformation which provides simple &lt;a href="https://github.com/uehaj/groovy-comprehension">list comprehension functionality&lt;/a> similar to that of Haskell, Scala or Python. Under the hood it’s a &amp;lsquo;monad&amp;rsquo; comprehension but you don&amp;rsquo;t care about it for use.&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>Guillaume Laforge gave a brand new presentation on Groovy, at Devoxx France 2014, covering the &lt;a href="https://speakerdeck.com/glaforge/groovy-in-2014-and-beyond-at-devoxx-france">novelties in the upcoming Groovy 2.3 release&lt;/a>&lt;/li>
&lt;li>Cédric Champeau spoke about &lt;a href="https://twitter.com/CedricChampeau/status/457176735717785601">Gradle vs Maven&lt;/a> at the Devoxx France 2014 conference last week. The slides are in French, but even if you&amp;rsquo;re not speaking it, you should get some interesting tidbits from the code samples and links&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>Andrey Bloschetsov published follow up benchmarks after Rick&amp;rsquo;s on the &lt;a href="https://github.com/bura/json-benchmarks">JSON performance improvements&lt;/a>, this time for the serialization of JSON payloads&lt;/li>
&lt;li>A &lt;a href="http://www.mscharhag.com/2014/04/groovy-23-introduces-traits.html">tutorial on Groovy traits&lt;/a> by Michael Scharhag&lt;/li>
&lt;li>Kyle Boon on the &lt;a href="http://kyleboon.org/blog/2014/04/17/code-coverage-with-groovy/">state of code coverage for Groovy&lt;/a>&lt;/li>
&lt;li>Marcin Gryszko&amp;rsquo;s &lt;a href="http://grysz.com/2014/04/22/quest-for-persistable-groovy-immutability/">quest for persistable Groovy immutability&lt;/a>&lt;/li>
&lt;li>Ken Kousen on the &amp;ldquo;&lt;a href="http://kousenit.wordpress.com/2014/04/16/the-closure-of-no-return/">closure of no return&lt;/a>&amp;rdquo;, on the fact you can &amp;ldquo;return&amp;rdquo; from the outer scope of the closure you pass to an each or findAll method&lt;/li>
&lt;li>Ken Kousen received some &lt;a href="http://kousenit.wordpress.com/2014/04/18/responses-to-the-closure-of-no-return/">excellent feedback on his article &amp;ldquo;the closure of no return&amp;rdquo;&lt;/a> showing the way towards the solution and best approaches&lt;/li>
&lt;li>&lt;a href="http://www.javacodegeeks.com/2013/04/grails-design-best-practices.html">Grails design best practices&lt;/a> by Nitin Kumar&lt;/li>
&lt;li>MrHaki on &lt;a href="http://mrhaki.blogspot.fr/2014/04/grails-goodness-extending-integratewith.html">extending the Grails IntegrateWith Command&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/ldaley/status/456705374775566337">Should Groovy support the Java 8 lambda syntax&lt;/a>? Your opinion is interesting.&lt;/li>
&lt;li>The Ratpack team is looking for &lt;a href="https://twitter.com/ratpackweb/status/457122433128873984">feedback on their async support&lt;/a>&lt;/li>
&lt;li>Cédric Champeau advises developers to &lt;a href="https://twitter.com/CedricChampeau/status/458548956025614336">always use the Gradle wrapper&lt;/a>, in particular within OSS projects, as it makes it easier to have a consistent build experience among developers and new contributors&lt;/li>
&lt;li>Schalk Cronjé is playing around with Greyton, &lt;a href="https://twitter.com/ysb33r/status/456189345075318785">a Groovy DSL (on top of JClouds)&lt;/a> to simplify cloud devops and is looking for feedback&lt;/li>
&lt;li>Schalk Cronjé is looking for &lt;a href="https://twitter.com/ysb33r/status/458558435869855744">volunteers to try the SMB / CIFS provider for his Groovy VFS&lt;/a> library&lt;/li>
&lt;li>Dierk König shares a handy &lt;a href="https://twitter.com/mittie/status/456189301202911232">Groovy one-liner to print your local IP address&lt;/a> from the command-line&lt;/li>
&lt;li>Dan Woods fell into the trap of &lt;a href="https://twitter.com/danveloper/status/456708683527778305">using GStrings as keys for maps&lt;/a>, and the follow up discussion on Twitter on the topic is worth a read&lt;/li>
&lt;li>Russ Miles finds &lt;a href="https://twitter.com/russmiles/status/456767251572858880">Groovy elegant and simple for using nested data structures&lt;/a>, adding to Spring Boot&amp;rsquo;s productivity too&lt;/li>
&lt;/ul>
&lt;h2 id="google-posts">Google+ posts&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://plus.google.com/103753917802203497881/posts/i2JE4WZa3A6">Monadic combinators&lt;/a> in Groovy by Mark Perry&lt;/li>
&lt;/ul>
&lt;h2 id="news">News&lt;/h2>
&lt;ul>
&lt;li>Jacob Aae Mikkelsen on the &lt;a href="http://grydeske.net/news/show/41">Grails Diary #16&lt;/a>&lt;/li>
&lt;li>Glu is now &lt;a href="https://twitter.com/glutweets/status/457587070886637568">hosting its binary release thanks to Bintray&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="books">Books&lt;/h2>
&lt;ul>
&lt;li>The &lt;a href="http://mrhaki.blogspot.fr/2014/04/grails-goodness-notebook-is-published.html">Grails Goodness book&lt;/a> has been published by MrHaki&lt;/li>
&lt;/ul>
&lt;h2 id="jobs">Jobs&lt;/h2>
&lt;ul>
&lt;li>James Foley is currently looking to network with an experienced (3 years plus) &lt;a href="http://sni-technology.jobs.net/job/Grails-Application-Developer/J3G74X69K69XRTS6QCQ/">Groovy/Grails Application Developer&lt;/a> for an excellent on going contract OR contract to hire scenario. 100% Remote opportunity!&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>The &lt;a href="https://twitter.com/mewzherder/status/456510357482131456">SpringOne2GX 2014 conference in Dallas is now open for registration&lt;/a>, with a super early bird price&lt;/li>
&lt;li>And of course, &lt;a href="http://gr8conf.eu/">GR8Conf Europe&lt;/a> and &lt;a href="http://gr8conf.us/">US 2014&lt;/a> are still open for registration too!&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>The Easter bunny is delivering the latest Groovy Weekly column!</p>
<p>With your chocolate eggs, you’ll get some bits of functional programming, a tutorial on Groovy traits, details of the JSON serialization performance improvements, and also get the chance of voicing your feedback on things like Groovy’s support of Java 8 lambda syntax, on the Ratpack async support, and more.</p>
<h2 id="releases">Releases</h2>
<ul>
<li>groovy-comprehension is a groovy extension module / AST transformation which provides simple <a href="https://github.com/uehaj/groovy-comprehension">list comprehension functionality</a> similar to that of Haskell, Scala or Python. Under the hood it’s a &lsquo;monad&rsquo; comprehension but you don&rsquo;t care about it for use.</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Guillaume Laforge gave a brand new presentation on Groovy, at Devoxx France 2014, covering the <a href="https://speakerdeck.com/glaforge/groovy-in-2014-and-beyond-at-devoxx-france">novelties in the upcoming Groovy 2.3 release</a></li>
<li>Cédric Champeau spoke about <a href="https://twitter.com/CedricChampeau/status/457176735717785601">Gradle vs Maven</a> at the Devoxx France 2014 conference last week. The slides are in French, but even if you&rsquo;re not speaking it, you should get some interesting tidbits from the code samples and links</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Andrey Bloschetsov published follow up benchmarks after Rick&rsquo;s on the <a href="https://github.com/bura/json-benchmarks">JSON performance improvements</a>, this time for the serialization of JSON payloads</li>
<li>A <a href="http://www.mscharhag.com/2014/04/groovy-23-introduces-traits.html">tutorial on Groovy traits</a> by Michael Scharhag</li>
<li>Kyle Boon on the <a href="http://kyleboon.org/blog/2014/04/17/code-coverage-with-groovy/">state of code coverage for Groovy</a></li>
<li>Marcin Gryszko&rsquo;s <a href="http://grysz.com/2014/04/22/quest-for-persistable-groovy-immutability/">quest for persistable Groovy immutability</a></li>
<li>Ken Kousen on the &ldquo;<a href="http://kousenit.wordpress.com/2014/04/16/the-closure-of-no-return/">closure of no return</a>&rdquo;, on the fact you can &ldquo;return&rdquo; from the outer scope of the closure you pass to an each or findAll method</li>
<li>Ken Kousen received some <a href="http://kousenit.wordpress.com/2014/04/18/responses-to-the-closure-of-no-return/">excellent feedback on his article &ldquo;the closure of no return&rdquo;</a> showing the way towards the solution and best approaches</li>
<li><a href="http://www.javacodegeeks.com/2013/04/grails-design-best-practices.html">Grails design best practices</a> by Nitin Kumar</li>
<li>MrHaki on <a href="http://mrhaki.blogspot.fr/2014/04/grails-goodness-extending-integratewith.html">extending the Grails IntegrateWith Command</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li><a href="https://twitter.com/ldaley/status/456705374775566337">Should Groovy support the Java 8 lambda syntax</a>? Your opinion is interesting.</li>
<li>The Ratpack team is looking for <a href="https://twitter.com/ratpackweb/status/457122433128873984">feedback on their async support</a></li>
<li>Cédric Champeau advises developers to <a href="https://twitter.com/CedricChampeau/status/458548956025614336">always use the Gradle wrapper</a>, in particular within OSS projects, as it makes it easier to have a consistent build experience among developers and new contributors</li>
<li>Schalk Cronjé is playing around with Greyton, <a href="https://twitter.com/ysb33r/status/456189345075318785">a Groovy DSL (on top of JClouds)</a> to simplify cloud devops and is looking for feedback</li>
<li>Schalk Cronjé is looking for <a href="https://twitter.com/ysb33r/status/458558435869855744">volunteers to try the SMB / CIFS provider for his Groovy VFS</a> library</li>
<li>Dierk König shares a handy <a href="https://twitter.com/mittie/status/456189301202911232">Groovy one-liner to print your local IP address</a> from the command-line</li>
<li>Dan Woods fell into the trap of <a href="https://twitter.com/danveloper/status/456708683527778305">using GStrings as keys for maps</a>, and the follow up discussion on Twitter on the topic is worth a read</li>
<li>Russ Miles finds <a href="https://twitter.com/russmiles/status/456767251572858880">Groovy elegant and simple for using nested data structures</a>, adding to Spring Boot&rsquo;s productivity too</li>
</ul>
<h2 id="google-posts">Google+ posts</h2>
<ul>
<li><a href="https://plus.google.com/103753917802203497881/posts/i2JE4WZa3A6">Monadic combinators</a> in Groovy by Mark Perry</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Jacob Aae Mikkelsen on the <a href="http://grydeske.net/news/show/41">Grails Diary #16</a></li>
<li>Glu is now <a href="https://twitter.com/glutweets/status/457587070886637568">hosting its binary release thanks to Bintray</a></li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>The <a href="http://mrhaki.blogspot.fr/2014/04/grails-goodness-notebook-is-published.html">Grails Goodness book</a> has been published by MrHaki</li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li>James Foley is currently looking to network with an experienced (3 years plus) <a href="http://sni-technology.jobs.net/job/Grails-Application-Developer/J3G74X69K69XRTS6QCQ/">Groovy/Grails Application Developer</a> for an excellent on going contract OR contract to hire scenario. 100% Remote opportunity!</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>The <a href="https://twitter.com/mewzherder/status/456510357482131456">SpringOne2GX 2014 conference in Dallas is now open for registration</a>, with a super early bird price</li>
<li>And of course, <a href="http://gr8conf.eu/">GR8Conf Europe</a> and <a href="http://gr8conf.us/">US 2014</a> are still open for registration too!</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #17</title><link>https://glaforge.dev/posts/2014/04/15/groovy-weekly-17/</link><pubDate>Tue, 15 Apr 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/04/15/groovy-weekly-17/</guid><description>&lt;p>Busy conference times:&lt;/p>
&lt;p>Greach is over, but we’re lucky to get the next batch of presentations and videos online on the &lt;a href="https://www.youtube.com/user/TheGreachChannel">Greach Youtube channel&lt;/a>.&lt;/p>
&lt;p>The &lt;a href="http://gr8conf.eu/">GR8Conf Europe&lt;/a> early bird price has been extended for another week.&lt;/p>
&lt;p>And Cédric and myself (Guillaume) are heading to the Devoxx France conference to speak about Groovy and Gradle.&lt;/p>
&lt;p>While at the same time, the Groovy team is delivering &lt;a href="https://glaforge.dev/posts/2014/04/10/second-beta-for-groovy-2-3/">a new beta for the lovely Groovy 2.3&lt;/a> release.&lt;/p></description><content:encoded>
<![CDATA[<p>Busy conference times:</p>
<p>Greach is over, but we’re lucky to get the next batch of presentations and videos online on the <a href="https://www.youtube.com/user/TheGreachChannel">Greach Youtube channel</a>.</p>
<p>The <a href="http://gr8conf.eu/">GR8Conf Europe</a> early bird price has been extended for another week.</p>
<p>And Cédric and myself (Guillaume) are heading to the Devoxx France conference to speak about Groovy and Gradle.</p>
<p>While at the same time, the Groovy team is delivering <a href="https://glaforge.dev/posts/2014/04/10/second-beta-for-groovy-2-3/">a new beta for the lovely Groovy 2.3</a> release.</p>
<p>Busy times, I tell you!</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Guillaume Laforge announces a <a href="https://glaforge.dev/posts/2014/04/10/second-beta-for-groovy-2-3/">second beta for Groovy 2.3.0</a></li>
<li><a href="https://twitter.com/gvmtool/status/454366295476416512">Groovy 2.3.0-beta-2</a> is available through GVM</li>
<li>A <a href="https://twitter.com/pledbrook/status/454523372756795392">Spring Boot Lazybones template</a> available</li>
</ul>
<h2 id="interviews">Interviews</h2>
<ul>
<li><a href="http://autentia.com/2014/04/15/las-entrevistas-de-greach-ya-disponibles">Interviews recorded at Greach</a>: Alberto Vilches, Russel Winder, Cédric Champeau, Graeme Rocher, Guillaume Laforge, Sébastien Blanc, Corrine Krych and others</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>You can watch all the <a href="https://storify.com/glaforge/greach-2014-videos-and-slides">videos and read the slides of all the presentations given at the Greach 2014 conference</a>, in Madrid (Spain)</li>
<li>Cédric Champeau presents &ldquo;<a href="http://greach.es/speakers/cedric-champeau-making-java-apis-groovy/">Making Java APIs Groovy</a>&rdquo; at Greach 2014</li>
<li>The <a href="http://greach.es/speakers/mario-garcia-the-functional-side-of-groovy/">functional side of Groovy</a>, by Mario Garcia, recorded at Greach 2014</li>
<li><a href="http://greach.es/speakers/david-dawson-architectural-flexibility-using-groovy/">Architectural flexibility with Groovy</a>, by David Dawson, at Greach 2014</li>
<li><a href="http://greach.es/speakers/ivan-lopez-metaprogramming-with-groovy/">Metaprogramming with Groovy</a>, by Ivan Lopez, at Greach 2014</li>
<li><a href="http://greach.es/speakers/marco-vermeulen-building-micro-services-using-spring-boot-and-friends/">Micro Service Architecture with Spring Boot, Groovy</a> and Friends, by Marco Vermeulen, at Greach 2014</li>
<li>Marco Vermeulen speaks about <a href="http://greach.es/speakers/marco-vermeulen-bdd-using-cucumber-jvm-and-groovy/">behavior-driven-development with Cucumber and Groovy</a>, at Greach 2014</li>
<li>Schalk Cronjé talks about <a href="http://greach.es/speakers/schalk-w-cronje-groovy-vfs/">Groovy VFS</a>, a humble DSL over the Apache VFS library</li>
<li>Russel Winder on <a href="http://greach.es/speakers/russel-winder-groovy-gpars-compilestatic-and-invokedynamic-and-java-8/">Groovy, GPars, @CompileStatic, invoke dynamic and Java 8</a>, recorded at Greach 2014</li>
<li><a href="http://greach.es/speakers/mario-garcia-workshop-gpars/">GPars workshop</a> presented at Greach 2014 by Mario Garcia</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Rick Hightower, who worked on the improved Groovy 2.3 JSON support, writes about the <a href="http://www.dzone.com/links/r/groovy_and_boon_provide_the_fastest_json_parser_f.html">high performance of Boon and Groovy JSON</a> compared to Jackson</li>
<li>Michael Scharhag on the <a href="http://www.mscharhag.com/2014/04/the-grails-depedency-injection.html">Grails dependency injection inheritance pitfall</a></li>
<li><a href="http://www.intelligrape.com/blog/2014/04/13/iterate-through-two-distinct-dates-groovy-2-2/">Iterate through dates</a> the Groovy way</li>
<li><a href="http://www.intelligrape.com/blog/2014/04/09/customizing-url-formats-in-grails/">Customizing URL formats in Grails</a>, making your URLs case insensitive</li>
<li>Making <a href="http://wordpress.transentia.com.au/wordpress/2014/04/09/artifactory-and-grails/">Grails and Artifactory</a> work together</li>
<li><a href="http://www.intelligrape.com/blog/2014/04/09/customizing-fusion-chart-for-various-type-via-grails/">Customizing fusion chart</a> for various type via Grails</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Tim Yates plays with <a href="https://gist.github.com/timyates/10257468">Groovy traits, combining them together</a> to create infinite iterators.</li>
<li>Tim Yates shows a <a href="https://gist.github.com/timyates/3722681">ping-pong hot-swapped untyped Akka actor in Groovy</a></li>
<li>Tim Yates crafted a <a href="https://gist.github.com/timyates/10470012">Pi approximation algorithm in Groovy on top of Akka</a> actors</li>
<li>Tim Yates uses <a href="https://gist.github.com/timyates/10474027">GPars for calculating an approximation of Pi</a></li>
<li>Craig Burke created a <a href="https://twitter.com/craigburke1/status/456062168547753984">sample Ratpack application</a> whose code you can look at on Github</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Cédric Champeau points at the <a href="https://twitter.com/cedricchampeau/status/454541228106084353">documentation and the blog posts he wrote about the new markup-based template engine</a></li>
<li>Guillaume Laforge is <a href="https://twitter.com/glaforge/status/454565615341408256">happy with the feedback the Groovy team is receiving</a> with the recent betas of Groovy 2.3. Feedback is really important to get the final release rock solid and ready for prime time!</li>
<li>The <a href="https://twitter.com/greach_es/status/455664662110289920">Greach conference Youtube channel</a> has more followers than on Twitter, thanks to all the conference presentations it published online</li>
<li>With our hight performant JSON stack, tail recursion, the NIO support, and more, coming in Groovy 2.3, or using GPars for concurrency needs, Dierk König notes that <a href="https://twitter.com/mittie/status/454704508430737408">adding Groovy to your projects might well make it faster</a>!</li>
<li>Robert Fletcher adds the <a href="https://twitter.com/rfletcherew/status/454529278273912832">&ldquo;trait&rdquo; keyword to the Prism JavaScript syntax highlighting</a> library</li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Luke Daley announces the upcoming <a href="http://forums.gradle.org/gradle/topics/after_1_12_comes_2_0">arrival of Gradle 2.0</a> after Gradle 1.12, upgrading to Groovy 2.</li>
<li>Successful test anr run of <a href="http://caelyf.ng.bluemix.net">Caelyf on IBM&rsquo;s BlueMix Cloud Foundry platform</a></li>
<li><a href="http://grydeske.net/news/show/40">Grails Diary week 15</a> by Jacob Aae Mikkelsen</li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li>A <a href="http://findgrailsjobs.com/job/526-grails-devops-developer">Grails / DevOps developer</a> at Retale, in Chicago, USA</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>The <a href="http://gr8conf.eu/">Early Bird price</a> (35% discount) for the GR8Conf Europe conference, in Denmark, has been extended till the 21st, it’s your last chance!</li>
<li>GR8Conf Europe announces <a href="https://twitter.com/gr8conf/status/454347531280211970">NineConsult as gold sponsor</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Second beta for Groovy 2.3</title><link>https://glaforge.dev/posts/2014/04/10/second-beta-for-groovy-2-3/</link><pubDate>Thu, 10 Apr 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/04/10/second-beta-for-groovy-2-3/</guid><description>&lt;p>Hot on the heels of our first beta for Groovy 2.3, here&amp;rsquo;s already a second one!&lt;/p>
&lt;p>We&amp;rsquo;ve received a lot of feedback, which is really awesome! Thanks a lot to all those who put the first beta to its pace and reported their findings.&lt;/p>
&lt;p>The most important change in this release is that the groovy.jar no longer depends on ASM and Antlr.&lt;/p>
&lt;p>It&amp;rsquo;s the first release ever of Groovy where the &lt;code>groovy.jar&lt;/code> itself is &lt;em>jajar-ed&lt;/em> like the &lt;code>groovy-all&lt;/code> JAR, which means that the ASM and Antlr libraries are embeded (and translated to a different package) so that you don&amp;rsquo;t need to depend on those libraries in your projects, and run the risk of a version clash with other frameworks or libraries depending on differing versions.&lt;/p></description><content:encoded>
<![CDATA[<p>Hot on the heels of our first beta for Groovy 2.3, here&rsquo;s already a second one!</p>
<p>We&rsquo;ve received a lot of feedback, which is really awesome! Thanks a lot to all those who put the first beta to its pace and reported their findings.</p>
<p>The most important change in this release is that the groovy.jar no longer depends on ASM and Antlr.</p>
<p>It&rsquo;s the first release ever of Groovy where the <code>groovy.jar</code> itself is <em>jajar-ed</em> like the <code>groovy-all</code> JAR, which means that the ASM and Antlr libraries are embeded (and translated to a different package) so that you don&rsquo;t need to depend on those libraries in your projects, and run the risk of a version clash with other frameworks or libraries depending on differing versions.</p>
<p>It&rsquo;s an important step for those who wish to have smaller JARs, and who don&rsquo;t want the whole libraries offered by Groovy, but who want to pick up just the components they are interested in.</p>
<p>Please report any problem you might find, in case you&rsquo;re using groovy.jar instead of groovy-all.jar.</p>
<p>Apart from that, several issues have been fixed, regarding traits, generics handling, a problem when running Groovy on Google App Engine.</p>
<p>There is a minor breaking change with regards to whitespace handling in our XML support.</p>
<p>You can check the JIRA release notes to see the issues fixed:<br />
<a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=20226">http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=20226</a></p>
<p>And head to our download area to get the latest and hottest beta: <a href="http://groovy.codehaus.org/Download?nc">http://groovy.codehaus.org/Download?nc</a></p>
<p>If you&rsquo;re using Gradle or Maven, we noticed some issues with our deployment process which made the groovy-all.jar lacking a pom.xml file on Central and JCenter. We&rsquo;re working on solving that issue with the JFrog team and we will let you know when it&rsquo;s fixed.</p>
<p>Keep up the feedback coming!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #16</title><link>https://glaforge.dev/posts/2014/04/08/groovy-weekly-16/</link><pubDate>Tue, 08 Apr 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/04/08/groovy-weekly-16/</guid><description>&lt;p>After the &lt;a href="https://plus.google.com/+GuillaumeLaforge/posts/ZcDcZ8iEvH8">Groovy knights&lt;/a> in Madrid, at the Greach conference, the Groovy team delivers the &lt;a href="https://glaforge.dev/posts/2014/04/04/a-beta-release-for-groovy-2-3/">first beta of Groovy 2.3&lt;/a>!&lt;/p>
&lt;p>An impressive feature list: including &lt;a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/documentation/core-traits.html">traits&lt;/a>, official JDK 8 support, tail recursion, NIO2 module, super fast JSON support, closure type inference, a new markup template engine, and much more.&lt;/p>
&lt;p>You will also notice the &lt;a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/documentation/">new documentation&lt;/a> shaping up but the Groovy Core team still looks forward to contributions to help with this gigantic task, so don’t hesitate to voice your will to help!&lt;/p></description><content:encoded>
<![CDATA[<p>After the <a href="https://plus.google.com/+GuillaumeLaforge/posts/ZcDcZ8iEvH8">Groovy knights</a> in Madrid, at the Greach conference, the Groovy team delivers the <a href="https://glaforge.dev/posts/2014/04/04/a-beta-release-for-groovy-2-3/">first beta of Groovy 2.3</a>!</p>
<p>An impressive feature list: including <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/documentation/core-traits.html">traits</a>, official JDK 8 support, tail recursion, NIO2 module, super fast JSON support, closure type inference, a new markup template engine, and much more.</p>
<p>You will also notice the <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/documentation/">new documentation</a> shaping up but the Groovy Core team still looks forward to contributions to help with this gigantic task, so don’t hesitate to voice your will to help!</p>
<p>Also double check the new style for <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/groovy-jdk/">GroovyDoc documentation</a> as well as <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/gapi/">GDK documentation</a>.</p>
<p>In the presentations section, don’t miss the bullet linking to all the excellent presentations given at the Greach conference.</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Guillaume Laforge announces the <a href="https://glaforge.dev/posts/2014/04/04/a-beta-release-for-groovy-2-3/">first beta of Groovy 2.3</a>, and details the new features</li>
<li><a href="https://twitter.com/gvmtool/status/452216920889389056">Groovy 2.3.0-beta-1</a> is available through GVM</li>
<li><a href="https://twitter.com/gvmtool/status/453146394489270272">Spring Boot 1.0.1</a> is available through GVM</li>
<li>Thomas Hirsch publishes a new <a href="https://github.com/thokari/gradle-geb-plugin">Gradle plugin for using Geb to automate build tasks</a></li>
<li><a href="https://twitter.com/gvmtool/status/452498582260940800">Glide 0.3.2</a> is available through GVM</li>
<li><a href="https://twitter.com/AndreyHihlovski/status/452169744360435712">Gretty</a>, the Gradle plugin for Jetty, released v0.0.15, with fast-reload resources, and is available from Maven Central and JCenter</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>You can watch all the <a href="https://storify.com/glaforge/greach-2014-videos-and-slides">videos and read the slides of all the presentations given at the Greach 2014 conference</a>, in Madrid (Spain)</li>
<li><a href="http://greach.es/speakers/guillaume-laforge-groovy-in-2014-and-beyond/">Groovy in 2014 and beyond!</a> — Guillaume Laforge</li>
<li><a href="http://greach.es/speakers/steve-pember-how-the-reactive-pattern-will-take-over-the-world-but-why-we-should-be-afraid-that-it-might-be-with-node-js/">Why reactive architecture will take over the world</a> — Steve Pember</li>
<li><a href="http://greach.es/speakers/steve-pember-richer-data-history-in-grails-with-event-sourcing/">Groovier Data History in Groovy with Event Sourcing</a> — Steve Pember</li>
<li><a href="http://greach.es/speakers/antonio-de-la-torre-grails-is-not-my-domain-model-where-do-i-have-to-put-my-business-logic/">Grails is not my domain model. Where do I put my Business Logic</a> — Antonio de la Torre</li>
<li><a href="http://greach.es/speakers/burt-beckwith-grails-worst-practices/">Grails Worst Practices</a> — Burt Beckwith</li>
<li><a href="http://greach.es/speakers/jeff-beck-experiences-using-grails-in-a-micro-service-architecture/">Experiences using Grails in a micro service architecture</a> — Jeff Beck</li>
<li><a href="http://greach.es/speakers/graeme-rocher-road-to-grails-3-0/">Road to Grails 3.0</a> — Graeme Rocher</li>
<li><a href="http://greach.es/speakers/alonso-torres-understanding-gorm/">Understanding GORM</a> — Alonso Torres</li>
<li><a href="http://greach.es/speakers/alvaro-sanchez-mariscal-creating-restful-apis-with-grails-and-spring-security/">Creating RESTful API’s with Grails and Spring Security</a> — Álvaro Sánchez-Mariscal</li>
<li><a href="http://greach.es/speakers/corinne-krych-fabrice-matrat-sebastien-blanc-hybrid-mobile-app-in-minutes-not-days-fast-and-furious-ii/">Hybrid Mobile App in minutes, not days: Fast and furious II</a> — Corinne Krych, Fabrice Matrat, Sébastien Blanc</li>
<li><a href="http://greach.es/speakers/james-page-startup-lesson-learned-from-rolling-a-grails-app-to-millions-users/">Startup Lesson Learned from rolling a Grails App to Millions Users</a> — James Page</li>
<li><a href="http://greach.es/speakers/david-dawson-hexagonal-architecture-in-grails/">Hexagonal Architecture in Grails</a> — David Dawson</li>
<li><a href="http://greach.es/speakers/alvaro-sanchez-mariscal-devqa-make-your-testers-happier-with-groovy-spock-and-geb/">DevQA: make your testers happier with Groovy, Spock and Geb</a> — Álvaro Sánchez-Mariscal</li>
<li><a href="http://greach.es/speakers/burt-beckwith-whats-new-in-spring-security-core-2-0/">What’s New in spring-security-core 2.0</a> — Burt Beckwith</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li><a href="http://devops.com/features/deployment-and-monitoring-automation-glu/">Glu</a>, the Groovy-based deployment and monitoring automation platform developed at LinkedIn, is featured in the DevOps.com website</li>
<li>Stéphane Maldini wrote an <a href="https://twitter.com/smaldini/status/451110890939875328">article on Reactor</a> for the NFJS magazine (req required)</li>
<li>Craig Atkinson created a <a href="http://www.objectpartners.com/2014/04/08/spock-mock-cheatsheet/">Spock cheat-sheet</a></li>
<li>An introduction to <a href="http://thejavatar.com/what-is-groovy/">what Groovy is</a></li>
<li>Corinne Krych blogs about her <a href="http://corinnekrych.blogspot.fr/2014/04/greach-2014-surprising-edition.html">impressions of the Greach</a> conference in Madrid</li>
<li>Jacob Aae Mikkelsen, of Grails Diary fame, gives us his <a href="http://grydeske.net/news/show/38">impressions from Greach</a> 2014</li>
<li><a href="http://www.mscharhag.com/2014/04/closure-composition-in-groovy.html">Closure composition</a> in Groovy by Michael Scharhag</li>
<li>Using the <a href="http://www.intelligrape.com/blog/2014/04/04/using-the-instagram-login-api-via-grails/">Instagram login API via Grails</a></li>
<li>Angel Ruiz explains how to <a href="http://aruizca.com/how-to-reference-a-file-inside-a-grails-plugin/">reference a file inside a Grails plugin</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Dierk König shows traits composition with an example of <a href="https://gist.github.com/Dierk/10010410">multiple traits inheritance</a></li>
<li>A <a href="https://twitter.com/springcentral/status/451414324893212672">micro Spring Boot app in Groovy</a>, in the span of a tweet, returning the device type used as JSON</li>
<li>Initial work has landed on an <a href="https://twitter.com/ratpackweb/status/452964565567827968">integrated async HTTP client in Ratpack</a>, which works great with RxJava</li>
<li>Macro Vermeulen used <a href="https://gist.github.com/marcoVermeulen/9994607">Geb as a &ldquo;wallpaper downloader&rdquo;</a> in this code snippet</li>
<li>MrHaki shows how to <a href="http://mrhaki.blogspot.fr/2014/04/groovy-goodness-converting-byte-array.html">convert byte arrays to hexadecimal strings</a></li>
<li>MrHaki blogs about <a href="http://mrhaki.blogspot.fr/2014/04/groovy-goodness-closure-as-writable.html">Groovy closures as Writable</a></li>
<li>MrHaki covers <a href="http://mrhaki.blogspot.fr/2014/04/groovy-goodness-gstring-as-writable.html">GStrings as Writable</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Dierk König distills the <a href="https://twitter.com/mittie/status/452814992522620928">essence of Groovy&rsquo;s nature</a> in the 140 characters of a tweet!</li>
<li>Dierk König remarks that Groovy 2.3 traits enable &ldquo;<a href="https://twitter.com/mittie/status/452813304176852992">design by capability</a>&rdquo;</li>
<li>Dan Woods notes that compared to Java 8 interface default methods, <a href="https://twitter.com/danveloper/status/453031225867767808">Groovy 2.3 traits are stateful</a></li>
<li>Marco Vermeulen is looking into Groovy&rsquo;s traits and enjoys the <a href="https://twitter.com/marcoVermeulen/status/452739684813524993">new tutorial-style Groovy documentation</a></li>
<li>Spock&rsquo;s Peter Niederwieser is wondering if Groovy 2.3&rsquo;s <a href="https://twitter.com/pniederw/status/451737521186410497">traits are the new killer feature for code reuse across specifications</a></li>
<li>Cédric Champeau announces that the <a href="https://twitter.com/CedricChampeau/status/451627694980345856">&ldquo;trait&rdquo; implementation has landed in the Groovy core repository</a></li>
<li>There&rsquo;s now a <a href="https://twitter.com/grainframework/status/451336989594619904">Gradle task to install Grain themes</a></li>
</ul>
<h2 id="news">News</h2>
<ul>
<li>Pivotal opened up a page for the <a href="http://www.gopivotal.com/support/oss">commercial support for their Open Source projects</a>, including Groovy and Grails coverage</li>
<li>With the release of the beta of Groovy 2.3, the <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/gapi/">GroovyDoc tool receives a face lift</a> and produce nicer looking documentation</li>
<li>The <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/groovy-jdk/">Groovy GDK documentation also gets a face lift</a> with a more stylish look</li>
<li>The brand <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/documentation/">new Groovy documentation</a>, written with Asciidoctor, is shaping up nicely, and the Groovy team welcomes contributions to help fill in the gaps</li>
<li>A <a href="https://twitter.com/gradleplugins">twitter account to follow about the latest Gradle plugin news</a>, by Andrés Almiray and his <a href="http://aalmiray.github.io/gradle-plugins/">Gradle plugin portal</a> effort</li>
<li>Vote for this <a href="http://youtrack.jetbrains.com/issue/IDEA-123484">feature request for IntelliJ IDEA to provide &ldquo;traits&rdquo; support</a> for the upcoming trait feature of Groovy 2.3</li>
<li>The Ratpack asks for feedback on whether to make <a href="https://twitter.com/ratpackweb/status/451852468398211072">Java 8 the base requirement for Ratpack apps</a></li>
<li>The Grails Diary <a href="http://grydeske.net/news/show/37">week 13</a> by Jacob Aae Mikkelsen</li>
<li>Another Grails Diary, <a href="http://grydeske.net/news/show/39">week 14</a>, by Jacob Aae Mikkelsen</li>
<li>Masato Nagai informs us that GBench, the Groovy benchmarking utility, as its own <a href="https://github.com/gperfutils/gbench/blob/master/src/main/groovy/groovyx/gbench/BenchmarkASTTransformation.groovy#L97">Groovy AST DSL</a>, which is faster than Groovy&rsquo;s own AstBuilder</li>
<li>SimpleCI renamed to <a href="https://github.com/DirectBuild/DirectBuild/wiki/News#april">DirectBuild</a>, and will soon be getting Remote Agent Support</li>
<li><a href="https://twitter.com/gvmtool/status/451236695992963072">Spring Boot 1.0</a> is available through GVM</li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li>BlueGlue, in London (UK), is seeking a <a href="https://twitter.com/FindGrailsJobs/status/451689367883776001">senior Grails / Java developer</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>Barely a week left before the end of the <a href="http://gr8conf.eu/">Early Bird price</a> (35% discount) for the GR8Conf Europe conference, in Denmark</li>
<li>The GR8Conf Europe organizers are announcing the <a href="https://storify.com/glaforge/gr8conf-europe-sponsors-announced">sponsors for the 2014 edition</a></li>
<li>Jon DeJong from Object Partners will present the <a href="http://www.meetup.com/Austin-Groovy-and-Grails-Users/events/166964742/">&ldquo;New Toolchain&rdquo; for building and testing single page Grails apps</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A beta release for Groovy 2.3</title><link>https://glaforge.dev/posts/2014/04/04/a-beta-release-for-groovy-2-3/</link><pubDate>Fri, 04 Apr 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/04/04/a-beta-release-for-groovy-2-3/</guid><description>&lt;p>The Groovy team is very pleased to announce the first beta of the Groovy 2.3 release!&lt;/p>
&lt;p>Groovy 2.3.0-beta-1 is actually already a feature-complete beta, so the release cycle towards the final version will be very short. Furthermore, Grails 2.4 is looking forward to integrating it rapidly as well for its upcoming major release too! So you can expect a general availability version of Groovy in the coming weeks, as we’ll move directly in “release candidate” mode after this beta. But your feedback, as usual, will be very important for trying out this beta in your respective projects, to tell us about any problem you might encounter.&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy team is very pleased to announce the first beta of the Groovy 2.3 release!</p>
<p>Groovy 2.3.0-beta-1 is actually already a feature-complete beta, so the release cycle towards the final version will be very short. Furthermore, Grails 2.4 is looking forward to integrating it rapidly as well for its upcoming major release too! So you can expect a general availability version of Groovy in the coming weeks, as we’ll move directly in “release candidate” mode after this beta. But your feedback, as usual, will be very important for trying out this beta in your respective projects, to tell us about any problem you might encounter.</p>
<p>In this email, below, I’ll give you a few pointers regarding the scope of the release, but we’ll be fleshing out a more detailed release notes document in the coming days and weeks with more information.</p>
<p>Warning: this is a lengthy email, but you’ll get to see all the new stuff and improvements the Groovy team and contributors have been working on!</p>
<p>The key highlights of Groovy 2.3 are:</p>
<ul>
<li>
<p>Official support for running Groovy on JDK 8  This is the first version of Groovy to be officially compatible with JDK 8.</p>
<p>JDK 8 and its interface default methods introduced some incompatibilities with a few methods of the Groovy Development Kit, so we had to adapt to the situation, introducing minor breaking changes for the affected methods and their outcome.</p>
<p>Note that we’re not planning to backport the changes to older versions of Groovy, so if you want to run Groovy on JDK 8, you’ll have to upgrade to the shiniest version of Groovy!</p>
</li>
<li>
<p>Traits A major highlight for Groovy 2.3 is the introduction of the concept of traits. <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-SNAPSHOT/html/documentation/core-traits.html">Traits</a> are reusable components of behavior that you can make your class implement, and are an additional Object-Oriented concept alongside classes and interfaces.</p>
<p>Groovy traits are stateful (unlike Java 8 interface default methods).</p>
<p>They allow the composition of behavior without going into the “diamond inheritance” problem allowing you to decide which behavior prevails upon conflict.</p>
<p>Traits support inheritance, thus a trait can extend another trait or implement an interface.</p>
<p>Traits are compatible with static type checking and compilation, as well as our usual dynamic behavior. Trait mixed-in methods are actually “real” methods (ie. visible from Java as well) and not just dynamic.</p>
<p>Traits can also be implemented at runtime with “as” or with “withTraits” if you just want to add behavior of a trait to an object you’re instantiating.</p>
<p>You can find more information on traits in the exhaustive <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-SNAPSHOT/html/documentation/core-traits.html">trait documentation</a>.</p>
</li>
<li>
<p>New and updated AST transformations</p>
</li>
<li>
<p><code>@TailRecursive</code> on methods adds tail recursion to methods which are recursive and call themselves at the last operation of the method body, which helps blowing up the stack with the recursive calls (<a href="http://jira.codehaus.org/browse/GROOVY-6570">GROOVY-6570</a>).</p>
</li>
<li>
<p><code>@Sortable</code> on classes implement comparison methods for you, according to the declaration order of your properties (<a href="http://jira.codehaus.org/browse/GROOVY-6649">GROOVY-6649</a>).</p>
</li>
<li>
<p><code>@Delegate</code> supports includes and excludes attributes so as to let you decide if certain methods should be included or excluded from the delegation (<a href="http://jira.codehaus.org/browse/GROOVY-6329">GROOVY-6329</a>).</p>
</li>
<li>
<p>New NIO module for Java 7+ On JDK 7 and beyond, you can benefit from the same methods as the ones of File, URLs, the various Stream classes, etc, but for the new NIO2 methods, like Path.</p>
<p>See <a href="http://jira.codehaus.org/browse/GROOVY-6377">GROOVY-6377</a> and the <a href="https://github.com/groovy/groovy-core/pull/260/files">pull request</a> for some further hints of the new methods.</p>
</li>
<li>
<p>Various minor performance improvements across the board, for static compilation, the “invoke dynamic” backend, as well as “normal” dynamic Groovy</p>
</li>
<li>
<p>Drastic JSON parsing and serialization performance improvements The Rick / Andrey duo spent a fair amount of time optimizing our JSON support, making Groovy 2.3’s JSON support usually faster than all the JSON libraries available in the Java ecosystem.</p>
<p>With <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/gapi/groovy/json/JsonSlurper.html">JsonSlurper</a>, you’ll be able to set different <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/gapi/groovy/json/JsonParserType.html">parser types</a> depending on the kind of input you wish to parse, particularly if you know the size of the payload you expect to parse, or whether you want a more tolerant parser which accepts elements like comments which are not normally supported by the JSON specification (<a href="http://jira.codehaus.org/browse/GROOVY-6546">GROOVY-6546</a>).</p>
<p>On the output front, with <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/gapi/groovy/json/JsonBuilder.html">JsonBuilder</a> and <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/gapi/groovy/json/StreamingJsonBuilder.html">StreamingJsonBuilder</a>, which both use <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/gapi/groovy/json/JsonOutput.html">JsonOutput</a> for output generation, the serialization has also been tremendously improved (<a href="http://jira.codehaus.org/browse/GROOVY-6554">GROOVY-6554</a>).</p>
</li>
<li>
<p>Closure type parameter inference We closed a gap which forced you to type your closure parameters to get correct type inference with static type checking or static compilation enabled. In situations like the following, you would have to explicitly give the type of the parameter, but it’s no longer required: [&lsquo;a&rsquo;,&lsquo;b&rsquo;].each { it.toUpperCase() } In the signature of your methods taking closures as arguments, you’ll also be able to annotate the closure parameter with <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/gapi/groovy/transform/stc/ClosureParams.html">@ClosureParams</a> to give additional hints to the type checker to infer the type of the parameters passed to your closure.</p>
<p>You can also find more about this in Cédric’s blog post on <a href="http://melix.github.io/blog/2014/01/closure_param_inference.html">closure parameter type inference</a>.</p>
</li>
<li>
<p>New Markup template engine Groovy now has an additional template engine, in the form of the Markup template engine, which gives you a very fast template engine, based on the familiar Markup builder approach and notation, but also offering formatting options (indentation, escaping), internationalization, includes, as well as proposing type checked templates and models.</p>
<p>More details about the new <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-SNAPSHOT/html/documentation/markup-template-engine.html">Markup template engine</a> in the documentation (<a href="http://jira.codehaus.org/browse/GROOVY-6596">GROOVY-6596</a>), as well as in Cédric’s <a href="http://melix.github.io/blog/">blog</a>, if you want to learn more about the “behind the scenes” stories!</p>
</li>
<li>
<p>Further Groovysh enhancements</p>
</li>
</ul>
<p>Along with some slightly reduced startup time, Groovysh has seen new improvements in its code-completion capabilities:</p>
<ul>
<li>
<p>line comment command (<a href="http://jira.codehaus.org/browse/GROOVY-6459">GROOVY-6459</a>)</p>
</li>
<li>
<p>completion for keywords (<a href="http://jira.codehaus.org/browse/GROOVY-6399">GROOVY-6399</a>)</p>
</li>
<li>
<p>completion for properties (<a href="http://jira.codehaus.org/browse/GROOVY-6395">GROOVY-6395</a>)</p>
</li>
<li>
<p>GroovyConsole enhancements  It is now possible to configure the font used by the console, and also to be able to run a selected snippet of code reusing the imports defined in your script making it easier to just run quick snippets of your script.</p>
</li>
<li>
<p>JUnit 4 GroovyAssert class The venerable <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/gapi/groovy/util/GroovyTestCase.html">GroovyTestCase</a> (JUnit 3 based approach) has often been used as a base class for your test classes — unless you’ve been using the <a href="http://www.spockframework.org/">Spock testing framework</a>, of course. One of the drawback of this class is that your test classes can’t extend your own classes, but must derive from GroovyTestCase to benefit from the additional assertion methods.</p>
<p>Groovy 2.3 introduces the JUnit 4-friendly <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/gapi/groovy/util/GroovyAssert.html">GroovyAssert</a>, which is a convenient class offering the usual assertion methods of GroovyTestCase, but in the form of static methods that you can static import in your test class.</p>
<p>We have not included the myriads of assertEquals method from GroovyTestCase as they have become useless with Groovy’s power assert, but it provides the handy shouldFail*() methods (<a href="http://jira.codehaus.org/browse/GROOVY-6588">GROOVY-6588</a>).</p>
</li>
<li>
<p>ConfigSlurper  ConfigSlurper just supported a single “environments” non-configurational conditional block, but you couldn’t let you define your own. With Groovy 2.3 you can also create your own ones too, for instance if you wanted to support “flavors” like OS variants (<a href="http://jira.codehaus.org/browse/GROOVY-6383">GROOVY-6383</a>).</p>
<p>In addition, the isSet() / hasSet() combo methods (<a href="http://jira.codehaus.org/browse/GROOVY-4639">GROOVY-4639</a>) have been added so you can double check if a given node of your configuration has been defined. Before, whether the node wasn’t defined or containing null, you couldn’t differentiate either case easily.</p>
</li>
<li>
<p><code>@BaseScript</code> class improvements  <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/gapi/groovy/transform/BaseScript.html">@BaseScript</a> is a fairly recent addition in Groovy, and it allowed to annotate a variable in your script to instruct the compiler to use a particular base script class for this script. Now we have another notation which is nicer as you can annotate an import or a package (<a href="http://jira.codehaus.org/browse/GROOVY-6592">GROOVY-6592</a>) to indicate that base script class:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@BaseScript</span><span style="color:#666">(</span>MyScript<span style="color:#666">)</span>  
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.transform.BaseScript</span> 
</span></span></code></pre></div><p>Additionally, base script classes can now use any abstract method for the script body. This means that you can implement the run() method to implement specific behavior like setup and tear down in tests (<a href="http://jira.codehaus.org/browse/GROOVY-6585">GROOVY-6585</a> and <a href="http://jira.codehaus.org/browse/GROOVY-6615">GROOVY-6615</a>).</p>
</li>
<li>
<p>New style for the GroovyDoc documentation of Groovy and the GDK methods GroovyDoc has been updated with a new fresh and modern skin that will be part of the future visual identity of the Groovy website. Those style updates are also available by default for your own usage of GroovyDoc, making your own documentation nicer on the eye.</p>
<p>You can have a look at the <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/gapi/">GroovyDoc documentation for Groovy 2.3.0-beta-1</a>.</p>
<p>We also took the opportunity to apply the same stylesheet to our “DocGenerator” tool which is responsible for the generation of the GDK documentation, showing the methods the Groovy library adds on top of the JDK classes.</p>
<p>Please also have a look at the new <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-beta-1/html/groovy-jdk/">restyled GDK documentation</a>.</p>
</li>
<li>
<p>New documentation We are still working on the brand <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-SNAPSHOT/html/documentation/">new documentation</a> for Groovy (in Asciidoc(tor) format), so you can already have a glimpse at what’s already covered or not.</p>
<p>We’re looking forward for help for fleshing out the various TBD (To Be Done) sections of the documentation, as it’s a gigantic taks to re-document each and every aspect of the language and its libraries! So please shout if you want to contribute to the new documentation! All help is warmly welcome!</p>
</li>
<li>
<p>Dependency upgrades</p>
<ul>
<li>Gradle 1.10 for building Groovy</li>
<li>ASM 5.0.1 library for generating our bytecode (also needed for our JDK 8 support)</li>
<li>JLine 2.11 and JANSI 1.11 library for Groovysh</li>
</ul>
</li>
</ul>
<p>Groovy wouldn’t be what it is without your support, your bug reports and feature requests, your contributions and pull requests, so big thanks to all those who contributed to this release in a way or another, including the usual suspects of the core team (Jochen, Paul, Cédric and myself), but also André (documentation, GroovyAssert, Groovy Console), Andres (Config slurper, @Sortable with Paul), Tim, Pascal (ConfigSlurper), Johanes (@TailRecursive), Paolo (new NIO module), Thibault (groovysh), Rick and Andrey (for the JSON support), Jim (base script class), Damien (GroovyDoc / GDK styling), Kenneth and several others I’ve forgotten to mention!</p>
<p>And in honor of this first beta release of Groovy 2.3, Cédric even decided to put his birthday on the same day, so happy birthday Cédric, and great work on this release!</p>
<p>We’re looking forward to your feedback running this beta to its pace!</p>
<p>It’s important to make this 2.3 release rock solid!</p>
<p><a href="http://groovy.codehaus.org/Download">Download Groovy 2.3-beta-1</a> and have a look at the <a href="http://jira.codehaus.org/browse/GROOVY-6366?jql=project%20%3D%20GROOVY%20AND%20fixVersion%20%3D%20%222.3.0-beta-1%22%20AND%20status%20in%20(Closed%2C%20Resolved)">JIRA release notes</a> for additional details on issues and bug fixes closed.</p>
<p>Keep on groovy’ing!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #15</title><link>https://glaforge.dev/posts/2014/04/01/groovy-weekly-15/</link><pubDate>Tue, 01 Apr 2014 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2014/04/01/groovy-weekly-15/</guid><description>&lt;p>Although it’s April Fools day, I’ll spare you with some funny incredible surprise, but I will instead focus a lot on all the great, interesting and inspiring things that happened at the Greach 2014 conference, in Spain, Madrid. Also don’t miss the various links about the incoming support of traits in Groovy 2.3!&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://spring.io/blog/2014/04/01/spring-boot-1-0-ga-released">Spring Boot 1.0&lt;/a> released, with its nice Groovy support&lt;/li>
&lt;li>&lt;a href="http://www.ratpack.io/versions/0.9.3">Ratpack 0.9.3&lt;/a> released&lt;/li>
&lt;li>&lt;a href="http://glide-gae.appspot.com">Glide 0.3.2 released&lt;/a>, with latest Gaelyk 2.1.1 and Google App Engine 1.9.1 into the mix&lt;/li>
&lt;li>A &lt;a href="https://twitter.com/marcovermeulen/status/448494455692079104">new version of the GVM SDK&lt;/a> was released&lt;/li>
&lt;li>The completely Groovy Continuous Integration Server, &lt;a href="https://github.com/DirectMyFile/SimpleCI">SimpleCI&lt;/a>, is now stable, and is on a rolling release cycle&lt;/li>
&lt;li>Sergei Egorov shares his &lt;a href="https://github.com/bsideup/MacroGroovy">MacroGroovy&lt;/a> project, to simplify the creation of AST transformations&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>Guillaume Laforge presented the &lt;a href="https://speakerdeck.com/glaforge/groovy-in-2014-and-beyond">present and future of Groovy&lt;/a> at Greach 2014&lt;/li>
&lt;li>&lt;a href="https://twitter.com/strsistemas/status/449832995089047552">Metaprogramming with Groovy&lt;/a>, presentation by Iván López at Greach 2014&lt;/li>
&lt;li>Jeff Beck presented his experience about using &lt;a href="http://beckje01.github.io/greach-2014-micro-service-talk/#/">Grails in microservice architecture&lt;/a>, at Greach 2014&lt;/li>
&lt;li>Marco Vermeulen demonstrated &lt;a href="https://twitter.com/marcovermeulen/status/449577949269082112">BDD with Cucumber and Groovy&lt;/a> at Greach 2014&lt;/li>
&lt;li>The slides and code from Greach 2014 of Marco Vermeulen&amp;rsquo;s &lt;a href="https://twitter.com/marcovermeulen/status/449936832340717568">Micro Services with Spring Boot and Groovy&lt;/a>&lt;/li>
&lt;li>A very funny but highly interesting presentation from Burt Beckwith, given at Greach 2014, about the &lt;a href="http://fr.slideshare.net/burtbeckwith/grails-worst-practices">worst Grails practices&lt;/a>&lt;/li>
&lt;li>Burt Beckwith detailed the &lt;a href="http://fr.slideshare.net/burtbeckwith/whats-newinspringsecurity">novelties in Grails spring-security-core 2.0&lt;/a>, in this Greach 2014 presentation&lt;/li>
&lt;li>Slides and code from Russel Winder&amp;rsquo;s Greach 2014 presentation on &lt;a href="https://twitter.com/russel_winder/status/449982413691949056">Groovy, GPars, @CompileStatic, &amp;ldquo;invoke dynamic&amp;rdquo; and Java 8&lt;/a>&lt;/li>
&lt;li>At Greach 2014, Alonso Torres presented &amp;ldquo;&lt;a href="http://fr.slideshare.net/alotor/understanding-gorm-greach-2014">understanding GORM&lt;/a>&amp;rdquo;&lt;/li>
&lt;li>Steve Pember presented on &lt;a href="http://fr.slideshare.net/StevePember/distributed-reactivearchitecture">reactive architectures&lt;/a> at Greach 2014&lt;/li>
&lt;li>Schalk Cronjé talked about &lt;a href="http://fr.slideshare.net/ysb33r/groovy-vfs-32889561">Groovy VFS&lt;/a> at Greach 2014&lt;/li>
&lt;li>An &lt;a href="https://twitter.com/JennStrater/status/449693392054652928">intro presentation of Groovy and Grails&lt;/a> by the &amp;ldquo;Gr8 ladies&amp;rdquo; of Minneapolis&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau is listening for &lt;a href="https://github.com/melix/groovy-core/blob/traits-implementation/src/spec/doc/core-traits.adoc">feedback on the upcoming Groovy 2.3 traits&lt;/a> implementation, don&amp;rsquo;t hesitate to share your thoughts! (the included snippets are not displayed but can be found in the sources)&lt;/li>
&lt;li>Brendon Anderson on a &lt;a href="http://www.objectpartners.com/2014/03/25/a-groovy-time-with-upnp-and-wemo/">Groovy time with UPnP and WeMo&lt;/a>&lt;/li>
&lt;li>MrHaki shows how to deal with &lt;a href="http://mrhaki.blogspot.fr/2014/03/grails-goodness-multiple-bootstraps.html">multiple Grails bootstraps&lt;/a>&lt;/li>
&lt;li>During his presentation at Greach 2014, Burt Beckwith mentioned a blog post about &lt;a href="http://burtbeckwith.com/blog/?p=2017">hash password conversion&lt;/a> he wrote&lt;/li>
&lt;li>David Estes shows how to &lt;a href="http://davydotcom.com/blog/2014-03-31-dynamically-finding-views-in-grails">dynamically find views in Grails&lt;/a>&lt;/li>
&lt;li>Some first impressions on &lt;a href="http://www.intelligrape.com/blog/2014/03/31/using-angularjs-with-grails-app-my-first-impression/">using Grails with Angular.JS&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau shows how he &lt;a href="https://twitter.com/CedricChampeau/status/449620913492094976">replaced @Mixin with the upcoming traits&lt;/a> from Groovy 2.3&lt;/li>
&lt;li>Examples of &lt;a href="https://github.com/melix/groovy-core/commit/41e21072a835b7bce2e9ac6bf4826518a4de616f#diff-148d63a5430b139a17b2261ca882e7a3R647">Groovy 2.3 runtime traits&lt;/a> support by Cédric Champeau&lt;/li>
&lt;li>A &lt;a href="https://github.com/ratpack/example-books/tree/master/src/main/groovy/ratpack/example/books">Ratpack 0.9.3 example&lt;/a> by Russell Hart, showing authentication at work with the pac4j module&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau &lt;a href="https://twitter.com/cedricchampeau/status/449609875170291713">notes&lt;/a> that &lt;a href="http://grooscript.org/">GrooScript&lt;/a> (the Groovy to JavaScript &amp;ldquo;transpiler&amp;rdquo;) would deserve more attention!&lt;/li>
&lt;li>If you want to &lt;a href="https://twitter.com/ratpackweb/status/449086252701069312">contribute to Ratpack&lt;/a>, here&amp;rsquo;s the list of contributions you can make&lt;/li>
&lt;li>Alonso Torres remarks the &lt;a href="https://twitter.com/alotor/status/449499586487664640">lack of Scalability talks about Grails at conferences&lt;/a>&amp;hellip; probably because Grails just scales :-)&lt;/li>
&lt;li>A big zoom of Cédric Champeau and Guillaume Laforge on the &lt;a href="https://twitter.com/sebi2706/status/449456237579866112">funny Groovy knights panel&lt;/a> at Greach 2014&lt;a href="https://twitter.com/gvmtool/status/449807218465320960">&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="other-news">Other news&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/449807218465320960">GVM welcomes Glide&lt;/a> as a new &amp;ldquo;candidate&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;h2 id="jobs">Jobs&lt;/h2>
&lt;ul>
&lt;li>A &lt;a href="http://www.meetup.com/Paris-Groovy-Grails/messages/66851332/">Groovy / Grails / Cassandra job in Paris&lt;/a>, France&lt;/li>
&lt;li>Biotech startup in Boston &lt;a href="https://twitter.com/burtbeckwith/status/448732435556335616">looking for a Grails developer&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>Who wants to be made a &lt;a href="https://twitter.com/CedricChampeau/status/449489765159960576">Groovy knight&lt;/a>? A nice panel where to put your heads was in the hallways of Greach 2014&lt;/li>
&lt;li>Yeray Darias Camacho took &lt;a href="https://www.flickr.com/photos/ydarias/sets/72157643259187873/">several notes on the presentations given at Greach&lt;/a> 2014, and published them on flickr&lt;/li>
&lt;li>Schalk Cronjé drawed many little &lt;a href="https://storify.com/glaforge/in-a-nutshell-graphical-notes-of-the-greach-2014">graphical notes about the sessions he attended at the Greach&lt;/a> 2014 conference&lt;/li>
&lt;li>A &lt;a href="http://www.meetup.com/San-Diego-Groovy-Grails-Group/">new Groovy/Grails user group in San Diego&lt;/a> has launched by Vahid Pazirandeh&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>Although it’s April Fools day, I’ll spare you with some funny incredible surprise, but I will instead focus a lot on all the great, interesting and inspiring things that happened at the Greach 2014 conference, in Spain, Madrid. Also don’t miss the various links about the incoming support of traits in Groovy 2.3!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://spring.io/blog/2014/04/01/spring-boot-1-0-ga-released">Spring Boot 1.0</a> released, with its nice Groovy support</li>
<li><a href="http://www.ratpack.io/versions/0.9.3">Ratpack 0.9.3</a> released</li>
<li><a href="http://glide-gae.appspot.com">Glide 0.3.2 released</a>, with latest Gaelyk 2.1.1 and Google App Engine 1.9.1 into the mix</li>
<li>A <a href="https://twitter.com/marcovermeulen/status/448494455692079104">new version of the GVM SDK</a> was released</li>
<li>The completely Groovy Continuous Integration Server, <a href="https://github.com/DirectMyFile/SimpleCI">SimpleCI</a>, is now stable, and is on a rolling release cycle</li>
<li>Sergei Egorov shares his <a href="https://github.com/bsideup/MacroGroovy">MacroGroovy</a> project, to simplify the creation of AST transformations</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Guillaume Laforge presented the <a href="https://speakerdeck.com/glaforge/groovy-in-2014-and-beyond">present and future of Groovy</a> at Greach 2014</li>
<li><a href="https://twitter.com/strsistemas/status/449832995089047552">Metaprogramming with Groovy</a>, presentation by Iván López at Greach 2014</li>
<li>Jeff Beck presented his experience about using <a href="http://beckje01.github.io/greach-2014-micro-service-talk/#/">Grails in microservice architecture</a>, at Greach 2014</li>
<li>Marco Vermeulen demonstrated <a href="https://twitter.com/marcovermeulen/status/449577949269082112">BDD with Cucumber and Groovy</a> at Greach 2014</li>
<li>The slides and code from Greach 2014 of Marco Vermeulen&rsquo;s <a href="https://twitter.com/marcovermeulen/status/449936832340717568">Micro Services with Spring Boot and Groovy</a></li>
<li>A very funny but highly interesting presentation from Burt Beckwith, given at Greach 2014, about the <a href="http://fr.slideshare.net/burtbeckwith/grails-worst-practices">worst Grails practices</a></li>
<li>Burt Beckwith detailed the <a href="http://fr.slideshare.net/burtbeckwith/whats-newinspringsecurity">novelties in Grails spring-security-core 2.0</a>, in this Greach 2014 presentation</li>
<li>Slides and code from Russel Winder&rsquo;s Greach 2014 presentation on <a href="https://twitter.com/russel_winder/status/449982413691949056">Groovy, GPars, @CompileStatic, &ldquo;invoke dynamic&rdquo; and Java 8</a></li>
<li>At Greach 2014, Alonso Torres presented &ldquo;<a href="http://fr.slideshare.net/alotor/understanding-gorm-greach-2014">understanding GORM</a>&rdquo;</li>
<li>Steve Pember presented on <a href="http://fr.slideshare.net/StevePember/distributed-reactivearchitecture">reactive architectures</a> at Greach 2014</li>
<li>Schalk Cronjé talked about <a href="http://fr.slideshare.net/ysb33r/groovy-vfs-32889561">Groovy VFS</a> at Greach 2014</li>
<li>An <a href="https://twitter.com/JennStrater/status/449693392054652928">intro presentation of Groovy and Grails</a> by the &ldquo;Gr8 ladies&rdquo; of Minneapolis</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Cédric Champeau is listening for <a href="https://github.com/melix/groovy-core/blob/traits-implementation/src/spec/doc/core-traits.adoc">feedback on the upcoming Groovy 2.3 traits</a> implementation, don&rsquo;t hesitate to share your thoughts! (the included snippets are not displayed but can be found in the sources)</li>
<li>Brendon Anderson on a <a href="http://www.objectpartners.com/2014/03/25/a-groovy-time-with-upnp-and-wemo/">Groovy time with UPnP and WeMo</a></li>
<li>MrHaki shows how to deal with <a href="http://mrhaki.blogspot.fr/2014/03/grails-goodness-multiple-bootstraps.html">multiple Grails bootstraps</a></li>
<li>During his presentation at Greach 2014, Burt Beckwith mentioned a blog post about <a href="http://burtbeckwith.com/blog/?p=2017">hash password conversion</a> he wrote</li>
<li>David Estes shows how to <a href="http://davydotcom.com/blog/2014-03-31-dynamically-finding-views-in-grails">dynamically find views in Grails</a></li>
<li>Some first impressions on <a href="http://www.intelligrape.com/blog/2014/03/31/using-angularjs-with-grails-app-my-first-impression/">using Grails with Angular.JS</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Cédric Champeau shows how he <a href="https://twitter.com/CedricChampeau/status/449620913492094976">replaced @Mixin with the upcoming traits</a> from Groovy 2.3</li>
<li>Examples of <a href="https://github.com/melix/groovy-core/commit/41e21072a835b7bce2e9ac6bf4826518a4de616f#diff-148d63a5430b139a17b2261ca882e7a3R647">Groovy 2.3 runtime traits</a> support by Cédric Champeau</li>
<li>A <a href="https://github.com/ratpack/example-books/tree/master/src/main/groovy/ratpack/example/books">Ratpack 0.9.3 example</a> by Russell Hart, showing authentication at work with the pac4j module</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Cédric Champeau <a href="https://twitter.com/cedricchampeau/status/449609875170291713">notes</a> that <a href="http://grooscript.org/">GrooScript</a> (the Groovy to JavaScript &ldquo;transpiler&rdquo;) would deserve more attention!</li>
<li>If you want to <a href="https://twitter.com/ratpackweb/status/449086252701069312">contribute to Ratpack</a>, here&rsquo;s the list of contributions you can make</li>
<li>Alonso Torres remarks the <a href="https://twitter.com/alotor/status/449499586487664640">lack of Scalability talks about Grails at conferences</a>&hellip; probably because Grails just scales :-)</li>
<li>A big zoom of Cédric Champeau and Guillaume Laforge on the <a href="https://twitter.com/sebi2706/status/449456237579866112">funny Groovy knights panel</a> at Greach 2014<a href="https://twitter.com/gvmtool/status/449807218465320960"></a></li>
</ul>
<h2 id="other-news">Other news</h2>
<ul>
<li><a href="https://twitter.com/gvmtool/status/449807218465320960">GVM welcomes Glide</a> as a new &ldquo;candidate&rdquo;</li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li>A <a href="http://www.meetup.com/Paris-Groovy-Grails/messages/66851332/">Groovy / Grails / Cassandra job in Paris</a>, France</li>
<li>Biotech startup in Boston <a href="https://twitter.com/burtbeckwith/status/448732435556335616">looking for a Grails developer</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>Who wants to be made a <a href="https://twitter.com/CedricChampeau/status/449489765159960576">Groovy knight</a>? A nice panel where to put your heads was in the hallways of Greach 2014</li>
<li>Yeray Darias Camacho took <a href="https://www.flickr.com/photos/ydarias/sets/72157643259187873/">several notes on the presentations given at Greach</a> 2014, and published them on flickr</li>
<li>Schalk Cronjé drawed many little <a href="https://storify.com/glaforge/in-a-nutshell-graphical-notes-of-the-greach-2014">graphical notes about the sessions he attended at the Greach</a> 2014 conference</li>
<li>A <a href="http://www.meetup.com/San-Diego-Groovy-Grails-Group/">new Groovy/Grails user group in San Diego</a> has launched by Vahid Pazirandeh</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #14</title><link>https://glaforge.dev/posts/2014/03/25/groovy-weekly-14/</link><pubDate>Tue, 25 Mar 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/03/25/groovy-weekly-14/</guid><description>&lt;p>This week is a Spanish week! The Groovy community is moving towards Madrid (Spain) for the &lt;a href="http://greach.es/">Greach conference&lt;/a>, to talk about Groovy, Grails, Gradle, Spock, GrooScript, GPars, Gaelyk and more! Enjoy the conference, for the lucky ones attending, and otherwise, have fun reading the news below.&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/musketyr/status/448151069378740226">Gaelyk 2.1 has been released&lt;/a>&lt;/li>
&lt;li>There&amp;rsquo;s a &lt;a href="http://sysgears.com/grain/news/grain-gradle-plugin-released/">Gradle plugin for the Grain&lt;/a> static website generator, for generating your site from your build&lt;/li>
&lt;li>&lt;a href="http://nextflow.io/">Nextflow&lt;/a>, a DSL for data-driven computational pipelines, based on Groovy and GPars, released in version 0.7&lt;/li>
&lt;li>Marco Vermeulen releases &lt;a href="https://github.com/marcoVermeulen/gradle-spawn-plugin">version 0.5.1 of the Gradle Spawn plugin&lt;/a>, for starting and stopping UNIX command line processes from within your build&lt;/li>
&lt;li>&lt;a href="http://grails.org/news/1289916">MongoDB GORM 2.0 plugin released&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>An old question on Quora about &lt;a href="http://www.quora.com/Is-Groovy-going-away/answer/Rob-Fletcher?__snids__=357550680&amp;amp;__nsrc__=4">whether &amp;ldquo;Groovy is going away&amp;rdquo;&lt;/a> is re-surfacing from time to time, and Rob Fletcher recently gave a new answer for that question.&lt;/li>
&lt;li>MrHaki is showing how to &lt;a href="http://mrhaki.blogspot.fr/2014/03/grails-goodness-using-groovy-sql.html">use Groovy SQL in Grails&lt;/a>&lt;/li>
&lt;li>Angel Ruiz writes about the &lt;a href="http://aruizca.com/migrating-from-grails-2-2-to-2-3/">Grails 2.2 to 2.3 migration&lt;/a>&lt;/li>
&lt;li>Alexander Reelsen believes &lt;a href="http://spinscale.github.io/2013-02-rapid-web-application-development-with-spark-groovy-jrebel.html">Spark, Groovy and JRebel make for a productive combination&lt;/a>&lt;/li>
&lt;li>A paper in German on &lt;a href="http://www.sigs.de/publications/newsletter/2014/03/breitfeld_kraft_soth_JS_02_14.pdf">using Grails for a portal framework&lt;/a>&lt;/li>
&lt;li>Libgdx, a Java based game framework, is &lt;a href="http://www.badlogicgames.com/wordpress/?p=3336">migrating to Gradle&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau has created a &lt;a href="https://gist.github.com/melix/9619800">Groovy solver for the 2048 game&lt;/a>, showing a nice use of Geb usage&lt;/li>
&lt;li>An example of &lt;a href="https://twitter.com/dailygrailstip/status/448358068166742016">broadcasting with websockets and Ratpack&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>Dierk König says it&amp;rsquo;s at the same time nice and weird to see &lt;a href="https://twitter.com/mittie/status/446292208048943104">Java developers now being able to enjoy the benefits that Groovy has provided for the past 10 years&lt;/a> already&lt;/li>
&lt;li>Replying to Rob Fletcher about the fact that the lambda syntax from Java 8 was differing in syntax from Groovy, Neill Robbins was saying that he &lt;a href="https://twitter.com/boardbloke_uk/status/446557532635807744">prefers the legibility of Groovy&amp;rsquo;s closures&lt;/a>. What&amp;rsquo;s your opinion?&lt;/li>
&lt;li>Jeff Brown is working on Grails with &lt;a href="https://twitter.com/jeffscottbrown/status/446673491052347392">Groovy AST transforms and type checking extensions and finds that it makes Groovy a very powerful programming tool&lt;/a>&lt;/li>
&lt;li>Dierk König remarks that &lt;a href="https://twitter.com/mittie/status/446675287489925120">Groovy can be at the same time provide more static checks than Java, but also be way more dynamic&lt;/a>&lt;/li>
&lt;li>Matt Stine shares a nice &lt;a href="https://twitter.com/mstine/status/446407804954624000">scaling demo app for Cloud Foundry using Spring Boot and Groovy&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="google-posts">Google+ posts&lt;/h2>
&lt;ul>
&lt;li>Richard Vowles &lt;a href="https://plus.google.com/u/0/+RichardVowles/posts/PNMikyJ6jYS">notes that with the latest IntelliJ IDEA 13.1 release, for your projects using the Maven Eclipse Groovy compiler&lt;/a>, there&amp;rsquo;s no need anymore for the Eclipse Groovy plugin, as IDEA recognizes the Maven layout properly now out of the box.&lt;/li>
&lt;/ul>
&lt;h2 id="other-news">Other news&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://grydeske.net/news/show/36">Grails Diary week #12&lt;/a> by Jacob Aae Mikkelsen&lt;/li>
&lt;/ul>
&lt;h2 id="books">Books&lt;/h2>
&lt;ul>
&lt;li>Book Review: &lt;a href="http://programmingitch.blogspot.dk/2014/03/book-review-programming-groovy-2.html">Programming Groovy 2 Dynamic Productivity for the Java Developer&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>Some of the &lt;a href="http://gr8conf.eu/?utm_source=glaforge&amp;amp;utm_medium=blog&amp;amp;utm_campaign=groovyweekly#/speakers">names of the speakers of GR8Conf Europe 2014&lt;/a>&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/springcentral/status/441693398152400896">Call for Papers of the SpringOne2GX 2014&lt;/a> conference will end on April 30th&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/gr8confus/status/446678741788278784">GR8Conf US 2014 tickets are now on sale&lt;/a>, and you can get door buster tickets the following two weeks&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>This week is a Spanish week! The Groovy community is moving towards Madrid (Spain) for the <a href="http://greach.es/">Greach conference</a>, to talk about Groovy, Grails, Gradle, Spock, GrooScript, GPars, Gaelyk and more! Enjoy the conference, for the lucky ones attending, and otherwise, have fun reading the news below.</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="https://twitter.com/musketyr/status/448151069378740226">Gaelyk 2.1 has been released</a></li>
<li>There&rsquo;s a <a href="http://sysgears.com/grain/news/grain-gradle-plugin-released/">Gradle plugin for the Grain</a> static website generator, for generating your site from your build</li>
<li><a href="http://nextflow.io/">Nextflow</a>, a DSL for data-driven computational pipelines, based on Groovy and GPars, released in version 0.7</li>
<li>Marco Vermeulen releases <a href="https://github.com/marcoVermeulen/gradle-spawn-plugin">version 0.5.1 of the Gradle Spawn plugin</a>, for starting and stopping UNIX command line processes from within your build</li>
<li><a href="http://grails.org/news/1289916">MongoDB GORM 2.0 plugin released</a></li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>An old question on Quora about <a href="http://www.quora.com/Is-Groovy-going-away/answer/Rob-Fletcher?__snids__=357550680&amp;__nsrc__=4">whether &ldquo;Groovy is going away&rdquo;</a> is re-surfacing from time to time, and Rob Fletcher recently gave a new answer for that question.</li>
<li>MrHaki is showing how to <a href="http://mrhaki.blogspot.fr/2014/03/grails-goodness-using-groovy-sql.html">use Groovy SQL in Grails</a></li>
<li>Angel Ruiz writes about the <a href="http://aruizca.com/migrating-from-grails-2-2-to-2-3/">Grails 2.2 to 2.3 migration</a></li>
<li>Alexander Reelsen believes <a href="http://spinscale.github.io/2013-02-rapid-web-application-development-with-spark-groovy-jrebel.html">Spark, Groovy and JRebel make for a productive combination</a></li>
<li>A paper in German on <a href="http://www.sigs.de/publications/newsletter/2014/03/breitfeld_kraft_soth_JS_02_14.pdf">using Grails for a portal framework</a></li>
<li>Libgdx, a Java based game framework, is <a href="http://www.badlogicgames.com/wordpress/?p=3336">migrating to Gradle</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Cédric Champeau has created a <a href="https://gist.github.com/melix/9619800">Groovy solver for the 2048 game</a>, showing a nice use of Geb usage</li>
<li>An example of <a href="https://twitter.com/dailygrailstip/status/448358068166742016">broadcasting with websockets and Ratpack</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Dierk König says it&rsquo;s at the same time nice and weird to see <a href="https://twitter.com/mittie/status/446292208048943104">Java developers now being able to enjoy the benefits that Groovy has provided for the past 10 years</a> already</li>
<li>Replying to Rob Fletcher about the fact that the lambda syntax from Java 8 was differing in syntax from Groovy, Neill Robbins was saying that he <a href="https://twitter.com/boardbloke_uk/status/446557532635807744">prefers the legibility of Groovy&rsquo;s closures</a>. What&rsquo;s your opinion?</li>
<li>Jeff Brown is working on Grails with <a href="https://twitter.com/jeffscottbrown/status/446673491052347392">Groovy AST transforms and type checking extensions and finds that it makes Groovy a very powerful programming tool</a></li>
<li>Dierk König remarks that <a href="https://twitter.com/mittie/status/446675287489925120">Groovy can be at the same time provide more static checks than Java, but also be way more dynamic</a></li>
<li>Matt Stine shares a nice <a href="https://twitter.com/mstine/status/446407804954624000">scaling demo app for Cloud Foundry using Spring Boot and Groovy</a></li>
</ul>
<h2 id="google-posts">Google+ posts</h2>
<ul>
<li>Richard Vowles <a href="https://plus.google.com/u/0/+RichardVowles/posts/PNMikyJ6jYS">notes that with the latest IntelliJ IDEA 13.1 release, for your projects using the Maven Eclipse Groovy compiler</a>, there&rsquo;s no need anymore for the Eclipse Groovy plugin, as IDEA recognizes the Maven layout properly now out of the box.</li>
</ul>
<h2 id="other-news">Other news</h2>
<ul>
<li><a href="http://grydeske.net/news/show/36">Grails Diary week #12</a> by Jacob Aae Mikkelsen</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>Book Review: <a href="http://programmingitch.blogspot.dk/2014/03/book-review-programming-groovy-2.html">Programming Groovy 2 Dynamic Productivity for the Java Developer</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>Some of the <a href="http://gr8conf.eu/?utm_source=glaforge&amp;utm_medium=blog&amp;utm_campaign=groovyweekly#/speakers">names of the speakers of GR8Conf Europe 2014</a></li>
<li>The <a href="https://twitter.com/springcentral/status/441693398152400896">Call for Papers of the SpringOne2GX 2014</a> conference will end on April 30th</li>
<li>The <a href="https://twitter.com/gr8confus/status/446678741788278784">GR8Conf US 2014 tickets are now on sale</a>, and you can get door buster tickets the following two weeks</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #13</title><link>https://glaforge.dev/posts/2014/03/18/groovy-weekly-13/</link><pubDate>Tue, 18 Mar 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/03/18/groovy-weekly-13/</guid><description>&lt;p>Hot on the heels of the Java 8 general availability good news, let’s have a look at the latest news of our beloved Groovy community!&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>Keegan Witt announces the &lt;a href="http://docs.codehaus.org/display/GMAVENPLUS/Releases">first enhancement release of GMavenPlus&lt;/a>&lt;/li>
&lt;li>Marco Vermeulen updates &lt;a href="https://bintray.com/vermeulen-mp/gvmtool/gvm-sdk/0.5.4/view">GVM SDK to 0.5.4&lt;/a>&lt;/li>
&lt;li>Andrés Almiray releases version 1.0 of his &lt;a href="https://twitter.com/aalmiray/status/444196810140430336">Gradle quickstart lazybones template&lt;/a>&lt;a href="http://docs.codehaus.org/display/GMAVENPLUS/Releases">&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>Jenn Strater speaks about the &lt;a href="http://jennstrater.blogspot.fr/2014/03/gr8ladies-mission.html">Gr8Ladies mission&lt;/a>&lt;/li>
&lt;li>Dan Woods explores &lt;a href="http://www.infoq.com/articles/microframeworks1-spring-boot">Spring Boot for micro-services and also shows the Groovy support&lt;/a> with Spring Boot CLI&lt;/li>
&lt;li>Tomás Lin&amp;rsquo;s &lt;a href="http://fbflex.wordpress.com/2014/03/13/spring-boot-websockets-stompjs-and-angularjs-a-few-notes/">notes on Spring Boot, Websockets, StompJS and AngularJS&lt;/a>&lt;/li>
&lt;li>Tomás Lin details how to &lt;a href="http://fbflex.wordpress.com/2014/03/14/building-web-content-jars-for-spring-boot-with-gradle/">build web content JARs for Spring Boot with Gradle&lt;/a>&lt;/li>
&lt;li>Michael Scharhag shows how you can &lt;a href="http://www.mscharhag.com/2014/03/groovy-shell.html">benefit from Groovysh&lt;/a>&lt;/li>
&lt;li>Tim Yates details the progress and &lt;a href="http://blog.bloidonia.com/post/79285969149/groovy-stream-updates-and-java-integration">Java 8 support of the Groovy Stream&lt;/a> library&lt;/li>
&lt;li>Alex Staveley writes about the &lt;a href="http://java.dzone.com/articles/good-use-closures">good use of closures&lt;/a>&lt;/li>
&lt;li>MrHaki explains how to &lt;a href="http://mrhaki.blogspot.fr/2014/03/grails-goodness-using-hibernate-native.html">use Hibernate native SQL queries in Grails&lt;/a>&lt;/li>
&lt;li>Matt Cholick shows how to &lt;a href="http://www.cholick.com/entry/show/276">use Gradle to build and apply AST transformations&lt;/a>&lt;/li>
&lt;li>MrHaki covers how to &lt;a href="http://mrhaki.blogspot.fr/2014/03/grails-goodness-combining-constraints.html">combine Grails constraints with shared constraints&lt;/a>&lt;/li>
&lt;li>An update on &lt;a href="http://thesolidsnake.wordpress.com/2014/02/28/whats-new-in-simple-jpa-0-6/">Griffon&amp;rsquo;s simple-jpa plugin&lt;/a>&lt;/li>
&lt;li>A &lt;a href="http://thesolidsnake.wordpress.com/2014/02/28/a-sample-application-for-simple-jpa-laundry/">sample Griffon application&lt;/a> with the simple-jpa plugin&lt;/li>
&lt;li>Using &lt;a href="http://asoftwareguy.com/2014/03/17/using-grails-rest-for-authentication-in-an-angularjs-spa/">Grails REST for authentication in an Angular.JS&lt;/a> single page application&lt;/li>
&lt;li>Mark Reinhold while mentioning those who contributed to the Java 8 release thanks our very own &lt;a href="http://mreinhold.org/blog/jdk8-ga">Cédric Champeau from the Groovy project for his useful bug reports for Java 8&lt;/a>&lt;/li>
&lt;li>Vivek Garg explains how to integrate &lt;a href="http://www.intelligrape.com/blog/2014/03/13/integrating-tumblr-with-grails-application/">Tumblr within Grails&lt;/a> applications&lt;/li>
&lt;li>Uday Pratap Singh shows how to &lt;a href="http://www.intelligrape.com/blog/2014/03/16/applying-layout-to-template-at-runtime-in-grails-application/">apply layout to template at runtime in Grails&lt;/a> applications&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>Baruch Sadogursky discusses &lt;a href="http://www.infoq.com/presentations/groovy-plugin-dsl">creating DSLs&lt;/a> which support plugins written both in Groovy or Java, addressing good public API design practices, security, and classpath isolation. Presentation given at SpringOne2GX 2013&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau created a &lt;a href="https://twitter.com/CedricChampeau/status/445967056245387264">solver for the addictive 2048 game using Geb and Groovy&lt;/a>&lt;/li>
&lt;li>Tim Yates plays with the &lt;a href="https://gist.github.com/timyates/9624727">RxJava&amp;rsquo;s Groovy support&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>A handful of d&lt;a href="https://twitter.com/gr8conf/status/445513675659821056">oor busters tickets for GR8Conf Europe&lt;/a> 2014 are still available&lt;/li>
&lt;li>MrHaki, alias Hubert Klein Hikkink, is starting work on a &lt;a href="https://twitter.com/mrhaki/status/443983670672908288">Grails goodness book&lt;/a>&lt;/li>
&lt;li>André Steingreß noticed &lt;a href="https://twitter.com/asteingr/status/446019973128146945">how light the documentation related to Ratpack&amp;rsquo;s Groovy support&lt;/a> is&amp;hellip; perhaps it&amp;rsquo;s time to help and contribute?&lt;/li>
&lt;li>Tip on how to &lt;a href="https://twitter.com/DailyGrailsTip/status/443235714239438848">set headers for a url.text&lt;/a> Groovy call&lt;/li>
&lt;li>If you want to learn more about &lt;a href="https://twitter.com/greach_es/status/444141558036590592">Gaelyk&amp;rsquo;s developments&lt;/a> from Gaelyk&amp;rsquo;s lead developer Vladimír Oraný&amp;rsquo;s own mouth, don&amp;rsquo;t miss this session from the Greach conference&lt;/li>
&lt;li>Sébastien Blanc shows a screenshot of a &lt;a href="https://twitter.com/sebi2706/status/443678260766212096">Grails backend using AeroGear unified push server&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="other-news">Other news&lt;/h2>
&lt;ul>
&lt;li>First &lt;a href="http://storify.com/glaforge/first-gr8conf-europe-2014-speakers-are-announced">speakers of the GR8Conf Europe&lt;/a> 2014 event are being announced&lt;/li>
&lt;li>Jacob Aee Mikkelsen’s weekly &lt;a href="http://grydeske.net/news/show/35">Grails diary&lt;/a>&lt;/li>
&lt;li>I forgot to mention the previous week &lt;a href="http://grydeske.net/news/show/33">Grails diary&lt;/a> in my latest Groovy Weekly column&lt;/li>
&lt;/ul>
&lt;h2 id="books">Books&lt;/h2>
&lt;ul>
&lt;li>You can support &lt;a href="https://leanpub.com/groovy-goodness-notebook">Hubert Klein Ikkink&amp;rsquo;s Groovy and Grails goodness&lt;/a> efforts by buying the Groovy goodness book&lt;/li>
&lt;li>Orest Ivasiv &lt;a href="http://www.halyph.com/2014/03/review-making-java-groovy.html">reviews Ken Kousen&amp;rsquo;s &amp;ldquo;Making Java Groovy&amp;rdquo;&lt;/a> book&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>A &lt;a href="http://www.eventbrite.com/e/gr8ladies-meetup-st-cloud-mn-tickets-10858087833?aff=twitter1">GR8Ladies meetup in Minneapolis&lt;/a>, MN (USA) on March 21st&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/Gr8Ladies/status/440298407874158593">GR8Ladies will be at Women Techmakers&lt;/a>, in Minneapolis, on March 22nd&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/springcentral/status/441693398152400896">Call for Papers of the SpringOne2GX 2014&lt;/a> conference will end on April 30th&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/greach_es/status/441591425487364097">Greach conference is offering 23 books and 27 ebooks&lt;/a> to win for the attendees&lt;/li>
&lt;li>Gradleware reminds that *you* could be a &lt;a href="https://twitter.com/Gradleware/status/440380894059044864">presenter at the Gradle Summit 2014&lt;/a>!&lt;/li>
&lt;li>Hubert Klein Ikkink (alias MrHaki) will be presenting a &lt;a href="https://twitter.com/jdriven_nl/status/440521630737911809">getting started Grails university session for the NLJUG&lt;/a> on March 25th&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>Hot on the heels of the Java 8 general availability good news, let’s have a look at the latest news of our beloved Groovy community!</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Keegan Witt announces the <a href="http://docs.codehaus.org/display/GMAVENPLUS/Releases">first enhancement release of GMavenPlus</a></li>
<li>Marco Vermeulen updates <a href="https://bintray.com/vermeulen-mp/gvmtool/gvm-sdk/0.5.4/view">GVM SDK to 0.5.4</a></li>
<li>Andrés Almiray releases version 1.0 of his <a href="https://twitter.com/aalmiray/status/444196810140430336">Gradle quickstart lazybones template</a><a href="http://docs.codehaus.org/display/GMAVENPLUS/Releases"></a></li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Jenn Strater speaks about the <a href="http://jennstrater.blogspot.fr/2014/03/gr8ladies-mission.html">Gr8Ladies mission</a></li>
<li>Dan Woods explores <a href="http://www.infoq.com/articles/microframeworks1-spring-boot">Spring Boot for micro-services and also shows the Groovy support</a> with Spring Boot CLI</li>
<li>Tomás Lin&rsquo;s <a href="http://fbflex.wordpress.com/2014/03/13/spring-boot-websockets-stompjs-and-angularjs-a-few-notes/">notes on Spring Boot, Websockets, StompJS and AngularJS</a></li>
<li>Tomás Lin details how to <a href="http://fbflex.wordpress.com/2014/03/14/building-web-content-jars-for-spring-boot-with-gradle/">build web content JARs for Spring Boot with Gradle</a></li>
<li>Michael Scharhag shows how you can <a href="http://www.mscharhag.com/2014/03/groovy-shell.html">benefit from Groovysh</a></li>
<li>Tim Yates details the progress and <a href="http://blog.bloidonia.com/post/79285969149/groovy-stream-updates-and-java-integration">Java 8 support of the Groovy Stream</a> library</li>
<li>Alex Staveley writes about the <a href="http://java.dzone.com/articles/good-use-closures">good use of closures</a></li>
<li>MrHaki explains how to <a href="http://mrhaki.blogspot.fr/2014/03/grails-goodness-using-hibernate-native.html">use Hibernate native SQL queries in Grails</a></li>
<li>Matt Cholick shows how to <a href="http://www.cholick.com/entry/show/276">use Gradle to build and apply AST transformations</a></li>
<li>MrHaki covers how to <a href="http://mrhaki.blogspot.fr/2014/03/grails-goodness-combining-constraints.html">combine Grails constraints with shared constraints</a></li>
<li>An update on <a href="http://thesolidsnake.wordpress.com/2014/02/28/whats-new-in-simple-jpa-0-6/">Griffon&rsquo;s simple-jpa plugin</a></li>
<li>A <a href="http://thesolidsnake.wordpress.com/2014/02/28/a-sample-application-for-simple-jpa-laundry/">sample Griffon application</a> with the simple-jpa plugin</li>
<li>Using <a href="http://asoftwareguy.com/2014/03/17/using-grails-rest-for-authentication-in-an-angularjs-spa/">Grails REST for authentication in an Angular.JS</a> single page application</li>
<li>Mark Reinhold while mentioning those who contributed to the Java 8 release thanks our very own <a href="http://mreinhold.org/blog/jdk8-ga">Cédric Champeau from the Groovy project for his useful bug reports for Java 8</a></li>
<li>Vivek Garg explains how to integrate <a href="http://www.intelligrape.com/blog/2014/03/13/integrating-tumblr-with-grails-application/">Tumblr within Grails</a> applications</li>
<li>Uday Pratap Singh shows how to <a href="http://www.intelligrape.com/blog/2014/03/16/applying-layout-to-template-at-runtime-in-grails-application/">apply layout to template at runtime in Grails</a> applications</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Baruch Sadogursky discusses <a href="http://www.infoq.com/presentations/groovy-plugin-dsl">creating DSLs</a> which support plugins written both in Groovy or Java, addressing good public API design practices, security, and classpath isolation. Presentation given at SpringOne2GX 2013</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Cédric Champeau created a <a href="https://twitter.com/CedricChampeau/status/445967056245387264">solver for the addictive 2048 game using Geb and Groovy</a></li>
<li>Tim Yates plays with the <a href="https://gist.github.com/timyates/9624727">RxJava&rsquo;s Groovy support</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>A handful of d<a href="https://twitter.com/gr8conf/status/445513675659821056">oor busters tickets for GR8Conf Europe</a> 2014 are still available</li>
<li>MrHaki, alias Hubert Klein Hikkink, is starting work on a <a href="https://twitter.com/mrhaki/status/443983670672908288">Grails goodness book</a></li>
<li>André Steingreß noticed <a href="https://twitter.com/asteingr/status/446019973128146945">how light the documentation related to Ratpack&rsquo;s Groovy support</a> is&hellip; perhaps it&rsquo;s time to help and contribute?</li>
<li>Tip on how to <a href="https://twitter.com/DailyGrailsTip/status/443235714239438848">set headers for a url.text</a> Groovy call</li>
<li>If you want to learn more about <a href="https://twitter.com/greach_es/status/444141558036590592">Gaelyk&rsquo;s developments</a> from Gaelyk&rsquo;s lead developer Vladimír Oraný&rsquo;s own mouth, don&rsquo;t miss this session from the Greach conference</li>
<li>Sébastien Blanc shows a screenshot of a <a href="https://twitter.com/sebi2706/status/443678260766212096">Grails backend using AeroGear unified push server</a></li>
</ul>
<h2 id="other-news">Other news</h2>
<ul>
<li>First <a href="http://storify.com/glaforge/first-gr8conf-europe-2014-speakers-are-announced">speakers of the GR8Conf Europe</a> 2014 event are being announced</li>
<li>Jacob Aee Mikkelsen’s weekly <a href="http://grydeske.net/news/show/35">Grails diary</a></li>
<li>I forgot to mention the previous week <a href="http://grydeske.net/news/show/33">Grails diary</a> in my latest Groovy Weekly column</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>You can support <a href="https://leanpub.com/groovy-goodness-notebook">Hubert Klein Ikkink&rsquo;s Groovy and Grails goodness</a> efforts by buying the Groovy goodness book</li>
<li>Orest Ivasiv <a href="http://www.halyph.com/2014/03/review-making-java-groovy.html">reviews Ken Kousen&rsquo;s &ldquo;Making Java Groovy&rdquo;</a> book</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>A <a href="http://www.eventbrite.com/e/gr8ladies-meetup-st-cloud-mn-tickets-10858087833?aff=twitter1">GR8Ladies meetup in Minneapolis</a>, MN (USA) on March 21st</li>
<li>The <a href="https://twitter.com/Gr8Ladies/status/440298407874158593">GR8Ladies will be at Women Techmakers</a>, in Minneapolis, on March 22nd</li>
<li>The <a href="https://twitter.com/springcentral/status/441693398152400896">Call for Papers of the SpringOne2GX 2014</a> conference will end on April 30th</li>
<li>The <a href="https://twitter.com/greach_es/status/441591425487364097">Greach conference is offering 23 books and 27 ebooks</a> to win for the attendees</li>
<li>Gradleware reminds that *you* could be a <a href="https://twitter.com/Gradleware/status/440380894059044864">presenter at the Gradle Summit 2014</a>!</li>
<li>Hubert Klein Ikkink (alias MrHaki) will be presenting a <a href="https://twitter.com/jdriven_nl/status/440521630737911809">getting started Grails university session for the NLJUG</a> on March 25th</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #12</title><link>https://glaforge.dev/posts/2014/03/11/groovy-weekly-12/</link><pubDate>Tue, 11 Mar 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/03/11/groovy-weekly-12/</guid><description>&lt;p>What can I tell you for this twelveth week? The Groovy ecosystem is still &lt;a href="http://groovy.codehaus.org/GSoc+2014">looking for students for the Google Summer of Code&lt;/a> program, so if you know students that could be interested, please pass the message. Also notice the nice release train of Grails with its recent 2.2 / 2.3 / 2.4 releases, congrats to the team!&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://grails.org/2.3.7%20Release%20Notes">Grails 2.3.7 release&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://sysgears.com/grain/news/grain-0-6-0-released/">Grain 0.6 released&lt;/a>&lt;/li>
&lt;li>Noam Tenne releases a &lt;a href="https://github.com/noamt/sgvm">Java API on top of GVM&lt;/a>&amp;rsquo;s API&lt;/li>
&lt;li>Andrés Almiray releases &lt;a href="https://twitter.com/aalmiray/status/441280820771565568">Gipsy 0.4&lt;/a> (Service Provider Interface simplification)&lt;/li>
&lt;li>Schalk Cronjé releases &lt;a href="https://github.com/ysb33r/groovy-vfs">v0.4 of his Groovy VFS library&lt;/a> (Virtual File System)&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/442751609303347202">GVM&amp;rsquo;s SDK&lt;/a> jumps to version 0.5.2&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>Marco Vermeulen blogs about the &lt;a href="http://www.wiredforcode.com/blog/2014/03/09/the-gvm-software-development-kit/">GVM Software Development Kit&lt;/a>&lt;/li>
&lt;li>David Estes introduces Karman, a standardized, multi-provider, easy-to-use, &lt;a href="http://www.davydotcom.com/blog/2014-03-10-introducing-karman">Grails based storage interface&lt;/a>, currently only supporting Amazon S3 but that should end up supporting also Rackspace CDN or Google Cloud Storage&lt;/li>
&lt;li>&lt;a href="http://thejavatar.com/what-is-groovy/">What is Groovy&lt;/a>? is the first article of the upcoming series encouraging programmers to start writing their applications in Groovy. This particular article provides a basic background of Groovy and a number of code snippets proving its supremacy over Java.&lt;/li>
&lt;li>Creating &lt;a href="http://sysgears.com/articles/creating-paginated-archives-in-grain/">paginated archives in Grain&lt;/a>&lt;/li>
&lt;li>Creating a custom website or &lt;a href="http://sysgears.com/articles/creating-a-custom-website-or-theme-with-grain/">theme with Grain&lt;/a>&lt;/li>
&lt;li>Tim Yates is enhancing his &lt;a href="https://twitter.com/tim_yates/status/441715082700263424">Groovy Stream library with some further Java 8 lambda support&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>If you speak Spanish, you might be interested in having a look at the &lt;a href="http://www.youtube.com/channel/UCgvO2jxLdGaVqHqnfPdyYzw">recordings of past Madrid GGUG meetups&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/ratpackweb/status/443480847501381633">Mixing Spring Boot and Ratpack&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>Ratpack is introducing the beginnings of &lt;a href="https://twitter.com/ratpackweb/status/441731677660778496">integration with Newrelic&lt;/a> for monitoring and tracing&lt;/li>
&lt;li>A &lt;a href="https://twitter.com/gvmtool/status/441702377100304384">GVM SDK library&lt;/a> for consumption of the GVM API&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/441133851667345408">Grails 2.2.5 is available on GVM&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/441701113847619585">Grails 2.3.7 is available through GVM&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/441133720897355776">Grails 2.4 milestone 1 is available on GVM&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="other-news">Other news&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau updates its ongoing &amp;ldquo;traits&amp;rdquo; implementation for Groovy 2.3 with &lt;a href="https://github.com/groovy/groovy-core/pull/343">trait inheritance support&lt;/a>. Don&amp;rsquo;t hesitate to try that and report back your findings!&lt;/li>
&lt;li>A project providing &lt;a href="https://github.com/draghuram/groovy-osgi-console">command line access to Virgo container using Groovy&lt;/a>&lt;/li>
&lt;li>Gretty, an advanced &lt;a href="https://github.com/akhikhl/gretty">Gradle plugin for running web-apps on Jetty&lt;/a>&lt;/li>
&lt;li>Cédric Champeau posted beta &lt;a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-SNAPSHOT/html/documentation/markup-template-engine.html">documentation for the new Markup template engine&lt;/a> for Groovy&lt;/li>
&lt;/ul>
&lt;h2 id="books">Books&lt;/h2>
&lt;ul>
&lt;li>The &lt;a href="https://twitter.com/bmuschko/status/439099709940957185">Gradle in Action print book is 43% off till March 20th&lt;/a> with a special promo code&lt;/li>
&lt;li>Lieven Doclo &lt;a href="http://java.dzone.com/articles/book-review-gradle-action">reviews the Gradle in Action book&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>A &lt;a href="http://www.eventbrite.com/e/gr8ladies-meetup-st-cloud-mn-tickets-10858087833?aff=twitter1">GR8Ladies meetup in Minneapolis&lt;/a>, MN (USA) on March 21st&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/springcentral/status/441693398152400896">Call for Papers of the SpringOne2GX 2014&lt;/a> conference will end on April 30th&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/greach_es/status/441591425487364097">Greach conference is offering 23 books and 27 ebooks&lt;/a> to win for the attendees&lt;/li>
&lt;li>Gradleware reminds that *you* could be a &lt;a href="https://twitter.com/Gradleware/status/440380894059044864">presenter at the Gradle Summit 2014&lt;/a>!&lt;/li>
&lt;li>Hubert Klein Ikkink (alias MrHaki) will be presenting a &lt;a href="https://twitter.com/jdriven_nl/status/440521630737911809">getting started Grails university session for the NLJUG&lt;/a> on March 25th&lt;/li>
&lt;li>The &lt;a href="https://twitter.com/Gr8Ladies/status/440298407874158593">GR8Ladies will be at Women Techmakers&lt;/a>, in Minneapolis, on March 22nd&lt;/li>
&lt;li>If you&amp;rsquo;re in the &lt;a href="http://sacgru.com">Sacremento area&lt;/a>, the SacGRU Groovy/Grails user group is meeting every first Thursday of each month at 18:30&lt;/li>
&lt;li>For those in the &lt;a href="https://groups.google.com/forum/#!forum/dfw2gug">Dallas / Fort Worth area&lt;/a>, you can attend the DFW Groovy/Grails user group on every first Wednesday of each month&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>What can I tell you for this twelveth week? The Groovy ecosystem is still <a href="http://groovy.codehaus.org/GSoc+2014">looking for students for the Google Summer of Code</a> program, so if you know students that could be interested, please pass the message. Also notice the nice release train of Grails with its recent 2.2 / 2.3 / 2.4 releases, congrats to the team!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://grails.org/2.3.7%20Release%20Notes">Grails 2.3.7 release</a></li>
<li><a href="http://sysgears.com/grain/news/grain-0-6-0-released/">Grain 0.6 released</a></li>
<li>Noam Tenne releases a <a href="https://github.com/noamt/sgvm">Java API on top of GVM</a>&rsquo;s API</li>
<li>Andrés Almiray releases <a href="https://twitter.com/aalmiray/status/441280820771565568">Gipsy 0.4</a> (Service Provider Interface simplification)</li>
<li>Schalk Cronjé releases <a href="https://github.com/ysb33r/groovy-vfs">v0.4 of his Groovy VFS library</a> (Virtual File System)</li>
<li><a href="https://twitter.com/gvmtool/status/442751609303347202">GVM&rsquo;s SDK</a> jumps to version 0.5.2</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Marco Vermeulen blogs about the <a href="http://www.wiredforcode.com/blog/2014/03/09/the-gvm-software-development-kit/">GVM Software Development Kit</a></li>
<li>David Estes introduces Karman, a standardized, multi-provider, easy-to-use, <a href="http://www.davydotcom.com/blog/2014-03-10-introducing-karman">Grails based storage interface</a>, currently only supporting Amazon S3 but that should end up supporting also Rackspace CDN or Google Cloud Storage</li>
<li><a href="http://thejavatar.com/what-is-groovy/">What is Groovy</a>? is the first article of the upcoming series encouraging programmers to start writing their applications in Groovy. This particular article provides a basic background of Groovy and a number of code snippets proving its supremacy over Java.</li>
<li>Creating <a href="http://sysgears.com/articles/creating-paginated-archives-in-grain/">paginated archives in Grain</a></li>
<li>Creating a custom website or <a href="http://sysgears.com/articles/creating-a-custom-website-or-theme-with-grain/">theme with Grain</a></li>
<li>Tim Yates is enhancing his <a href="https://twitter.com/tim_yates/status/441715082700263424">Groovy Stream library with some further Java 8 lambda support</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>If you speak Spanish, you might be interested in having a look at the <a href="http://www.youtube.com/channel/UCgvO2jxLdGaVqHqnfPdyYzw">recordings of past Madrid GGUG meetups</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li><a href="https://twitter.com/ratpackweb/status/443480847501381633">Mixing Spring Boot and Ratpack</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Ratpack is introducing the beginnings of <a href="https://twitter.com/ratpackweb/status/441731677660778496">integration with Newrelic</a> for monitoring and tracing</li>
<li>A <a href="https://twitter.com/gvmtool/status/441702377100304384">GVM SDK library</a> for consumption of the GVM API</li>
<li><a href="https://twitter.com/gvmtool/status/441133851667345408">Grails 2.2.5 is available on GVM</a></li>
<li><a href="https://twitter.com/gvmtool/status/441701113847619585">Grails 2.3.7 is available through GVM</a></li>
<li><a href="https://twitter.com/gvmtool/status/441133720897355776">Grails 2.4 milestone 1 is available on GVM</a></li>
</ul>
<h2 id="other-news">Other news</h2>
<ul>
<li>Cédric Champeau updates its ongoing &ldquo;traits&rdquo; implementation for Groovy 2.3 with <a href="https://github.com/groovy/groovy-core/pull/343">trait inheritance support</a>. Don&rsquo;t hesitate to try that and report back your findings!</li>
<li>A project providing <a href="https://github.com/draghuram/groovy-osgi-console">command line access to Virgo container using Groovy</a></li>
<li>Gretty, an advanced <a href="https://github.com/akhikhl/gretty">Gradle plugin for running web-apps on Jetty</a></li>
<li>Cédric Champeau posted beta <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0-SNAPSHOT/html/documentation/markup-template-engine.html">documentation for the new Markup template engine</a> for Groovy</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>The <a href="https://twitter.com/bmuschko/status/439099709940957185">Gradle in Action print book is 43% off till March 20th</a> with a special promo code</li>
<li>Lieven Doclo <a href="http://java.dzone.com/articles/book-review-gradle-action">reviews the Gradle in Action book</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>A <a href="http://www.eventbrite.com/e/gr8ladies-meetup-st-cloud-mn-tickets-10858087833?aff=twitter1">GR8Ladies meetup in Minneapolis</a>, MN (USA) on March 21st</li>
<li>The <a href="https://twitter.com/springcentral/status/441693398152400896">Call for Papers of the SpringOne2GX 2014</a> conference will end on April 30th</li>
<li>The <a href="https://twitter.com/greach_es/status/441591425487364097">Greach conference is offering 23 books and 27 ebooks</a> to win for the attendees</li>
<li>Gradleware reminds that *you* could be a <a href="https://twitter.com/Gradleware/status/440380894059044864">presenter at the Gradle Summit 2014</a>!</li>
<li>Hubert Klein Ikkink (alias MrHaki) will be presenting a <a href="https://twitter.com/jdriven_nl/status/440521630737911809">getting started Grails university session for the NLJUG</a> on March 25th</li>
<li>The <a href="https://twitter.com/Gr8Ladies/status/440298407874158593">GR8Ladies will be at Women Techmakers</a>, in Minneapolis, on March 22nd</li>
<li>If you&rsquo;re in the <a href="http://sacgru.com">Sacremento area</a>, the SacGRU Groovy/Grails user group is meeting every first Thursday of each month at 18:30</li>
<li>For those in the <a href="https://groups.google.com/forum/#!forum/dfw2gug">Dallas / Fort Worth area</a>, you can attend the DFW Groovy/Grails user group on every first Wednesday of each month</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #11</title><link>https://glaforge.dev/posts/2014/03/04/groovy-weekly-11/</link><pubDate>Tue, 04 Mar 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/03/04/groovy-weekly-11/</guid><description>&lt;p>This is a bit of a special day for me today, as it’s my first daughter’s birthday, who just reached 6, a much younger age than my 10 years involvement in Groovy! And after eating a home-made vanilla French yogurt birthday cake, I’m happy to share with you the following news bits, in particular the nice releases of Groovy 2.2.2, of the first milestone of Grails 2.4, as well as of Ratpack 0.9.2.&lt;/p></description><content:encoded>
<![CDATA[<p>This is a bit of a special day for me today, as it’s my first daughter’s birthday, who just reached 6, a much younger age than my 10 years involvement in Groovy! And after eating a home-made vanilla French yogurt birthday cake, I’m happy to share with you the following news bits, in particular the nice releases of Groovy 2.2.2, of the first milestone of Grails 2.4, as well as of Ratpack 0.9.2.</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Cédric Champeau announces the <a href="http://groovy.markmail.org/thread/kssk4ti7kpf5qxcf">release of Groovy 2.2.2</a>, done through our new build and delivery infrastructure, built with JetBrains&rsquo; TeamCity on the server they are sponsoring for us, delivered through JFrog&rsquo;s Artifactory and Bintray pipeline, towards JCenter and Maven Central for the hosting</li>
<li>Graeme Rocher announces the <a href="http://grails.org/2.4.0.M1+Release+Notes">first milestone release of Grails 2.4</a>, with Groovy 2.2, Spring Framework 4, the asset pipeline, and static compilation</li>
<li><a href="http://www.ratpack.io/versions/0.9.2">Ratpack 0.9.2 released</a></li>
<li>Carlos Garcia released a <a href="https://github.com/carlosgsouza/gradle-console">Gradle Groovy Console plugin</a> which allows you to run a Groovy Console and lets you play with your code and dependencies</li>
<li>Al Baker publishes version <a href="https://twitter.com/AlBaker_Dev/status/440555501106249728">0.7.2 of his Groovy Sparql</a> library</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>An article on the <a href="http://techblog.netflix.com/2014/03/the-netflix-dynamic-scripting-platform.html">Netflix Dynamic Scripting Platform</a>, which is using Groovy as its scripting language</li>
<li>David Turanski on <a href="https://spring.io/blog/2014/03/03/groovy-bean-configuration-in-spring-framework-4">Groovy bean configuration in Spring Framework 4</a></li>
<li>Tomás Lin shows how to <a href="http://fbflex.wordpress.com/2014/02/28/spring-boot-jar-cloud-foundry/">deploy Spring Boot Groovy scripts as a Jar file in Cloud Foundry</a></li>
<li>Jean-Baptiste Nizet writes about <a href="http://blog.ninja-squad.com/2014/02/25/rest-api-doc-with-asciidoctor-and-gradle/">generating REST API documentation with Asciidoctor and Gradle</a>. Also note that he could have used the <a href="https://github.com/asciidoctor/asciidoctor-gradle-plugin">Gradle Asciidoctor plugin.</a></li>
<li>David Estes writes about <a href="http://www.davydotcom.com/blog/2014-02-25-faster-asset-compilation-with-grails-asset-pipeline">faster asset compilation with Grails asset-pipeline</a> plugin</li>
<li><a href="http://www.intelligrape.com/blog/2014/02/27/groovy-integration-with-cq5-in-maven/">Groovy Integration with Adobe CQ5</a> and Maven</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Venkat Subramaniam speaks about the <a href="https://www.agilelearner.com/presentation/124">DRY principle in Grails</a></li>
<li>Dan Woods presented about <a href="http://fr.slideshare.net/danveloper/practical-rest-in-grails-23">practical REST in Grails 2.3</a></li>
<li>Brian Kotek advises on creating <a href="http://www.infoq.com/presentations/grails-ria">RIA applications in Grails</a> using an array of technologies, including JavaScript, CoffeeScript, TypeScript, ExtJS, Dojo, REST, JSON, GSON, GORM, etc.</li>
</ul>
<h2 id="screencasts">Screencasts</h2>
<ul>
<li>Bobby Warner demonstrates the <a href="http://www.bobbywarner.com/2014/02/26/grails-asset-pipeline-plugin/">Grails asset pipeline through a screencast</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Tim Yates provides some examples of <a href="https://gist.github.com/renatoathaydes/5078535#comment-1184256">usage of his Groovy Stream library</a> in this Haskell vs Groovy comparison Gist</li>
<li>A script showing how easy it is to use <a href="https://gist.github.com/kdabir/9332408">GStorm and Groovy to crunch and consume data from REST APIs</a></li>
<li>Cédric Champeau provides a sample <a href="https://github.com/melix/spring-groovymarkup">integration of the upcoming new markup template engine of Groovy 2.3 with Spring Boot</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li><a href="https://twitter.com/gvmtool/status/438809878812172288">Groovy 2.2.2 is available through GVM</a></li>
<li>André Steingreß points at the <a href="https://twitter.com/asteingr/status/439652445116182528">Groovy support in Github&rsquo;s Atom text editor</a></li>
<li>Dierk König says <a href="https://twitter.com/mittie/status/439740263427506176">Groovy experience makes for a perfect training in Java 8 lambdas</a></li>
<li>Russell Hart shows a sample of a <a href="https://twitter.com/rus_hart/status/439765546796462081">realtime metrics dashboard built with Ratpack</a> and Websockets</li>
<li>A conversation triggered by Collin Harrington on <a href="https://twitter.com/ColinHarrington/status/438046837241806848">Grails @Transactional</a></li>
<li>Ludovic Champenois tweeted about the <a href="https://twitter.com/ludoch/status/438870147017633793">new Gradle plugin for Google App Engine</a> which also works great in Android Studio</li>
<li>Chanwit Kaewkasi reports the creation of a <a href="https://twitter.com/chanwit/status/440909970448449536">20-node Spark Hadoop cluster and is scripts it with the Groovy Console</a></li>
<li><a href="https://twitter.com/gvmtool/status/440766140122673152">Spring Boot 1.0-rc4 is available through GVM</a></li>
<li>Benoit Hediard releases the <a href="https://twitter.com/benorama/status/440851116436488192">CDN asset pipeline Grails plugin</a> to publish assets into CDNs</li>
<li>Marco Vermeulen <a href="https://twitter.com/marcoVermeulen/status/440854403059159040">updates his Gradle Heroku plugin</a></li>
</ul>
<h2 id="google-posts">Google+ posts</h2>
<ul>
<li>Kunal Dabir shares a script to see <a href="https://plus.google.com/u/0/+KunalDabir/posts/VFwUAve5DNM?cfem=1">how Groovy developer friendly is your machine</a></li>
</ul>
<h2 id="mailing-list-discussions">Mailing-list discussions</h2>
<ul>
<li>An ongoing discussion on <a href="http://gradle.1045684.n5.nabble.com/upgrade-gradle-to-groovy-2-td5712332.html">Gradle&rsquo;s upgrade path towards Groovy 2</a> instead of the old 1.8.6</li>
</ul>
<h2 id="other-news">Other news</h2>
<ul>
<li>Andrés Almiray is launching a <a href="http://aalmiray.github.io/gradle-plugins/">Gradle plugin portal</a></li>
<li>Maciek Opała wrote me to tell us about a <a href="https://github.com/apphance/Apphance-MobilE-Build-Automation">mobile build automation solution for iOS, Android and Windows phone</a>, in the form of a Gradle plugin</li>
<li>The excellent <a href="http://grydeske.net/news/show/32">Grails Diary</a>, like every Sunday, from Jacob Aae Mikkelsen</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>Till March 11th, you get <a href="http://campaign.r20.constantcontact.com/render?llr=gimslwbab&amp;v=001QGVxMVttOOarDTh0jflYxJN1lCcc6sQJTBmK4WKxc8Bf8ucTp1ky44eTr6BtW7_IyYa8R-ZU-huk-pSyoVSuKqXXHLwUdZj_Cd8_eddHh7a_gmMEsP9BrnXnSEKpzLTYYC3fWFCEbw4%3D">50% off of Gradle in Action</a> and other selected books</li>
<li>The <a href="https://twitter.com/bmuschko/status/439099709940957185">Gradle in Action print book is 43% off till March 20th</a> with a special promo code</li>
<li>A short Amazon <a href="http://www.amazon.com/review/R1NHVXJREXYDIT">review of the Grails 2: Quick Start Guide</a> book from Dave Klein</li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li>Netflix is <a href="https://twitter.com/robspieldenner/status/438513787058921472">hiring Gradle build and plugin developers</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>Gradleware reminds that *you* could be a <a href="https://twitter.com/Gradleware/status/440380894059044864">presenter at the Gradle Summit 2014</a>!</li>
<li>Hubert Klein Ikkink (alias MrHaki) will be presenting a <a href="https://twitter.com/jdriven_nl/status/440521630737911809">getting started Grails university session for the NLJUG</a> on March 25th</li>
<li>The <a href="https://twitter.com/Gr8Ladies/status/440298407874158593">GR8Ladies will be at Women Techmakers</a>, in Minneapolis, on March 22nd</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #10</title><link>https://glaforge.dev/posts/2014/02/25/groovy-weekly-10/</link><pubDate>Tue, 25 Feb 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/02/25/groovy-weekly-10/</guid><description>&lt;p>Vacations won’t stop me from updating you about all the latest developments in the Groovy community! In this short introduction, I’d like to bring your attention to the fact that the Groovy ecosystem got accepted as an organization for the Google Summer of Code program. So if you are a student, or know students, or engineering schools, universities, etc, interested in contributing to projects of the Groovy ecosystem, please don’t hesitate to spread the word!&lt;/p></description><content:encoded>
<![CDATA[<p>Vacations won’t stop me from updating you about all the latest developments in the Groovy community! In this short introduction, I’d like to bring your attention to the fact that the Groovy ecosystem got accepted as an organization for the Google Summer of Code program. So if you are a student, or know students, or engineering schools, universities, etc, interested in contributing to projects of the Groovy ecosystem, please don’t hesitate to spread the word!</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Andrés Almiray releases v0.0.1 of the <a href="https://github.com/aalmiray/livereload-gradle-plugin">LiveReload plugin for Gradle</a></li>
<li>Ongoing work on the <a href="https://github.com/rundis/LightTable-Groovy/releases/tag/0.0.2">LightTable IDE support for Groovy</a></li>
<li>Vladimír Oraný releases a <a href="https://github.com/metadataregistry/simple-fixtures">Simple Fixtures</a> project, similar to Grails Fixture plugin</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>A tutorial on using <a href="https://spring.io/guides/gs/accessing-data-gorm/">GORM from a Spring Boot</a> application</li>
<li>Magnus Rundberget blogs about how he&rsquo;s implementing <a href="http://codewader.blogspot.no/2014/02/a-groovy-light-table-client-step-2.html">Groovy code evaluation in his Light Table</a> Groovy plugin</li>
<li>Howard Lewis Ship explains how to <a href="http://tapestryjava.blogspot.com/2014/02/gradle-overruling-third-party.html">overrule third-party dependencies</a> in case of conflicts with Gradle</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Bobby Warner discusses the features available in <a href="http://www.infoq.com/presentations/grails-rest-api">Grails 2.3 to build RESTful APIs</a>, at SpringOne2GX 2013</li>
<li>Jeff Beck posted his slides of the presentation he made at the Groovy MN user group about <a href="http://beckje01.github.io/gum-2014-cassandra-grails-talk/#/">Grails and Cassandra</a></li>
<li>Andrey Cheptsov explores some of the <a href="http://www.infoq.com/presentations/groovy-grails-intellij-idea">IntelliJ IDEA’s tricky features helping Groovy&amp;Grails developers</a> to be more productive. Recorded at SpringOne2GX 2013.</li>
</ul>
<h2 id="screencasts">Screencasts</h2>
<ul>
<li>Dan Vega gives an <a href="http://www.danvega.org/blog/2014/2/20/Intro-to-Spring-Security-Core-for-Grails#">intro to Spring Security core for Grails</a></li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Stéphane Maldini shows an <a href="https://github.com/smaldini/ratpack-reactor-gh-sample/blob/master/src/ratpack/Ratpack.groovy">integration of Ratpack and Reactor</a> for asynchronous REST</li>
<li>Kunal Dabir releases version 0.5 of Gstorm, a <a href="https://github.com/kdabir/gstorm/blob/master/examples/using_annotations.groovy">mini-ORM for relational databases</a>, and shows an example with the new annotations</li>
<li>An <a href="https://gist.github.com/kaendfinger/9183112">AST Transformation for debugging scripts</a> which prints information at the beginning of the script, before each statement, and the end of the script</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Guillaume Laforge announces the <a href="https://twitter.com/glaforge/status/438228999903674368">participation of the Groovy community to the Google Summer of Code</a> program</li>
<li>Guillaume Laforge <a href="https://twitter.com/glaforge/status/438232563925417985">calls for students who want to contribute to the Groovy ecosystem</a> during the Google Summer of Code. Please spread the word, if you know students, schools or universities willing to participate!</li>
<li>Tim Yates shares a <a href="https://twitter.com/tim_yates/status/437962657061040128">Spring Boot project using GORM</a> configured with Hibernate and Mysql</li>
<li>Dan Woods updates his <a href="https://twitter.com/danveloper/status/437756649453809664">Gradle Provisioning plugin with ISO building capability</a> and registering as HVM AMIs at Amazon</li>
<li><a href="https://twitter.com/gvmtool/status/436653545358884864">Spring Boot 1.0 rc-3</a> is available through GVM</li>
<li>The <a href="https://twitter.com/gradleware/status/436643232978448384">Gradle Summit 2014 is opening its Call for Papers</a></li>
<li>A tweet from Dierk König saying that <a href="https://twitter.com/mittie/status/436914778611929088">GPars is put to good use</a>, with its Agent, DataFlow, KanbanFlow and async functions, in <a href="http://open-dolphin.org/dolphin_website/Home.html">OpenDolphin</a></li>
<li>Magnus Rundberget celebrates the 330th downloads of the <a href="https://twitter.com/mrundberget/status/438221883163828224">Gradle Buster.JS plugin</a>, to automate running Buster.JS tests from CI and local development</li>
</ul>
<h2 id="other-news">Other news</h2>
<ul>
<li>Keegan Witt published the <a href="http://docs.codehaus.org/display/GMAVENPLUS/Home/">GMaven+ documentation</a> on Codehaus&rsquo; wiki</li>
<li>Cédric Champeau developed a <a href="https://github.com/melix/teamcity-groovy-buildstep">Groovy build step plugin for TeamCity</a>, for the needs of the Groovy project infrastructure</li>
<li>John Engelman mentions the <a href="http://grails.github.io/grails-gradle-plugin/docs/manual/">new documentation for the Grails Gradle plugin</a></li>
<li>Jacob Aae Mikkelsen publishes the <a href="http://grydeske.net/news/show/31">Grails Diary</a> with the usual updates on all things Grails</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li><a href="https://twitter.com/manningbooks/status/436871815164887041">Gradle in Action is now available in print</a>, and soon in ePub &amp; Kindle edition too</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>Still <a href="http://us4.campaign-archive1.com/?u=ac7af4c02d6cec67fe3198a63&amp;id=bc02eb8bb2&amp;e=88c97e251c">doorbusters tickets available for GR8Conf Europe 2014</a>, be fast to get the best price!</li>
<li>Gradle Summit 2014 is <a href="http://www.gradle.org/gradle-summit-cfp">calling for presenters</a>!</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #9</title><link>https://glaforge.dev/posts/2014/02/18/groovy-weekly-9/</link><pubDate>Tue, 18 Feb 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/02/18/groovy-weekly-9/</guid><description>&lt;p>An interesting column this week thanks to the major release of Griffon 1.5, but also a minor release of Grails 2.3.6 which brings the often requested standalone GORM support!&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://grails.org/2.3.6+Release+Notes">Grails 2.3.6&lt;/a> released, including GORM standalone support&lt;/li>
&lt;li>&lt;a href="http://docs.codehaus.org/pages/viewpage.action?pageId=237371944">Griffon 1.5&lt;/a> is released&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau details in a two parts article his work on a &lt;a href="http://melix.github.io/blog/2014/02/markuptemplateengine.html">new template engine for Groovy&lt;/a>, that is fast, can be type checked and statically compiled&lt;/li>
&lt;li>Second part of the article from Cédric Champeau about his new markup template engine for Groovy with &lt;a href="http://melix.github.io/blog/2014/02/markuptemplateengine_part2.html">advanced explanations about static type checking extensions and static compilation&lt;/a>&lt;/li>
&lt;li>More on the &lt;a href="http://codewader.blogspot.no/2014/02/a-groovy-light-table-client-step-1.html">Groovy client for the Light-Table&lt;/a> IDE&lt;/li>
&lt;li>André Steingreß covers &lt;a href="http://blog.andresteingress.com/2014/02/14/grails-java-based-spring-config/">Java-based configuration with Grails&lt;/a>&lt;/li>
&lt;li>Marco Vermeulen explains how to &lt;a href="http://www.wiredforcode.com/blog/2014/02/11/deploy-to-heroku-with-gradle">deploy apps like Ratpack, Spring Boot or Dropwizard on Heroku&lt;/a> thanks to Gradle&lt;/li>
&lt;li>VoltDB integrates &lt;a href="http://voltdb.com/voltdbgroovy">Groovy for its datastore inline procedure language&lt;/a>&lt;/li>
&lt;li>An article on RelProxy, a project for a &lt;a href="http://java.dzone.com/articles/presenting-relproxy-hot-class">hot class reloader and scripting&lt;/a> for Java and Groovy&lt;/li>
&lt;li>&lt;a href="http://octodecillion.com/blog/use-java-enum-to-switch-on-class-types/">Enum to Switch on Class types&lt;/a> in Java and Groovy&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>Ken Kousen presents &lt;a href="http://www.infoq.com/presentations/advanced-groovy-tips">advanced Groovy features&lt;/a>, such as closure coercion, mixins, simple runtime metaprogramming, operator overloading, drop and take, overlooked methods in the Groovy JDK, and more (recorded at SpringOne2GX 2013)&lt;/li>
&lt;li>Greg Turnquist demoes using &lt;a href="http://www.infoq.com/presentations/grails-case-study">Spring Mail, Security, REST, GridFS, Bootstrap and jQuery in a production grade Grails application&lt;/a>&lt;/li>
&lt;li>John Engelman presents a talk on &lt;a href="https://speakerdeck.com/johnrengelman/enterprise-grails-spring-batch">enterprise Grails with Spring Batch&lt;/a> for batch processing&lt;/li>
&lt;/ul>
&lt;h2 id="mailing-list-discussions">Mailing-list discussions&lt;/h2>
&lt;ul>
&lt;li>Rick Hightower&amp;rsquo;s been working on the new JSON parser for Groovy 2.3 to &lt;a href="http://groovy.329449.n5.nabble.com/Improve-serialization-speeds-of-JsonOutput-td5718316.html#a5718394">give Groovy the fastest JSON parser available on the JVM&lt;/a>, and along with Andrey Blochestov they are also looking at making the creation of JSON payloads also super fast&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>Andrés Almiray on the &lt;a href="https://twitter.com/aalmiray/status/434438930436730880">simplicity of functional testing for Griffon applications&lt;/a>&lt;/li>
&lt;li>Miguel de la Cruz shows his experiment with a &lt;a href="https://github.com/mgdelacroix/ccServer/blob/master/ccServer.groovy">minimal web server implemented with Ratpack&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/433731764134363136">Griffon 1.5&lt;/a> available through GVM&lt;/li>
&lt;li>GVM now offers &lt;a href="https://twitter.com/gvmtool/status/433280770166509568">Gradle 1.11&lt;/a> too&lt;/li>
&lt;li>Johannes Link, contributor of the @TailRecursive transformation for Groovy 2.3, compared the performance of @TailRecursive on methods vs trampoline() on closures, and indicate that the &lt;a href="https://twitter.com/johanneslink/status/434057275146522625">performance advantage is for @TailRecursive&lt;/a>&lt;/li>
&lt;li>Dave Syer shows an example of a &lt;a href="https://twitter.com/david_syer/status/365367862170353667">Spring Boot controller written in Groovy that outputs JSON&lt;/a>&lt;/li>
&lt;li>Bobby Warner notes that the &lt;a href="https://twitter.com/bobbywarner/status/433277222476980224">Asset pipeline plugin replaces the resources plugin&lt;/a> by default from Grails 2.4 onward&lt;/li>
&lt;/ul>
&lt;h2 id="other-news">Other news&lt;/h2>
&lt;ul>
&lt;li>The &lt;a href="https://github.com/groovy/groovy-core/pull/315">@TailRecursive transformation&lt;/a> has been contributed by Johannes Link and Cédric Champeau merged the pull request into the Groovy 2.3 branch&lt;/li>
&lt;li>Jacob Aae Mikkelsen&amp;rsquo;s Week #7 of the &lt;a href="http://grydeske.net/news/show/30">Grails diary&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="books">Books&lt;/h2>
&lt;ul>
&lt;li>The final version of the &lt;a href="http://www.manning.com/muschko/">Gradle in Action&lt;/a> eBook is available&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://gr8conf.eu/">Registration for GR8Conf Europe 2014 is open&lt;/a>&lt;/li>
&lt;li>You can find the &lt;a href="http://lanyrd.com/2014/greach/">Greach conference on Lanyard&lt;/a>&lt;/li>
&lt;li>A new &lt;a href="http://www.meetup.com/Warszawa-Java-User-Group-Warszawa-JUG/events/166258012/?utm_content=buffer5ada7&amp;amp;utm_medium=social&amp;amp;utm_source=twitter.com&amp;amp;utm_campaign=buffer">Groovy user group in Warsaw&lt;/a>, Poland&lt;/li>
&lt;li>Peter Ledbrook is speaking about why your build matters, and the &lt;a href="https://skillsmatter.com/meetups/6202-why-does-your-build-matter">importance of Gradle&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>An interesting column this week thanks to the major release of Griffon 1.5, but also a minor release of Grails 2.3.6 which brings the often requested standalone GORM support!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://grails.org/2.3.6+Release+Notes">Grails 2.3.6</a> released, including GORM standalone support</li>
<li><a href="http://docs.codehaus.org/pages/viewpage.action?pageId=237371944">Griffon 1.5</a> is released</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Cédric Champeau details in a two parts article his work on a <a href="http://melix.github.io/blog/2014/02/markuptemplateengine.html">new template engine for Groovy</a>, that is fast, can be type checked and statically compiled</li>
<li>Second part of the article from Cédric Champeau about his new markup template engine for Groovy with <a href="http://melix.github.io/blog/2014/02/markuptemplateengine_part2.html">advanced explanations about static type checking extensions and static compilation</a></li>
<li>More on the <a href="http://codewader.blogspot.no/2014/02/a-groovy-light-table-client-step-1.html">Groovy client for the Light-Table</a> IDE</li>
<li>André Steingreß covers <a href="http://blog.andresteingress.com/2014/02/14/grails-java-based-spring-config/">Java-based configuration with Grails</a></li>
<li>Marco Vermeulen explains how to <a href="http://www.wiredforcode.com/blog/2014/02/11/deploy-to-heroku-with-gradle">deploy apps like Ratpack, Spring Boot or Dropwizard on Heroku</a> thanks to Gradle</li>
<li>VoltDB integrates <a href="http://voltdb.com/voltdbgroovy">Groovy for its datastore inline procedure language</a></li>
<li>An article on RelProxy, a project for a <a href="http://java.dzone.com/articles/presenting-relproxy-hot-class">hot class reloader and scripting</a> for Java and Groovy</li>
<li><a href="http://octodecillion.com/blog/use-java-enum-to-switch-on-class-types/">Enum to Switch on Class types</a> in Java and Groovy</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Ken Kousen presents <a href="http://www.infoq.com/presentations/advanced-groovy-tips">advanced Groovy features</a>, such as closure coercion, mixins, simple runtime metaprogramming, operator overloading, drop and take, overlooked methods in the Groovy JDK, and more (recorded at SpringOne2GX 2013)</li>
<li>Greg Turnquist demoes using <a href="http://www.infoq.com/presentations/grails-case-study">Spring Mail, Security, REST, GridFS, Bootstrap and jQuery in a production grade Grails application</a></li>
<li>John Engelman presents a talk on <a href="https://speakerdeck.com/johnrengelman/enterprise-grails-spring-batch">enterprise Grails with Spring Batch</a> for batch processing</li>
</ul>
<h2 id="mailing-list-discussions">Mailing-list discussions</h2>
<ul>
<li>Rick Hightower&rsquo;s been working on the new JSON parser for Groovy 2.3 to <a href="http://groovy.329449.n5.nabble.com/Improve-serialization-speeds-of-JsonOutput-td5718316.html#a5718394">give Groovy the fastest JSON parser available on the JVM</a>, and along with Andrey Blochestov they are also looking at making the creation of JSON payloads also super fast</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Andrés Almiray on the <a href="https://twitter.com/aalmiray/status/434438930436730880">simplicity of functional testing for Griffon applications</a></li>
<li>Miguel de la Cruz shows his experiment with a <a href="https://github.com/mgdelacroix/ccServer/blob/master/ccServer.groovy">minimal web server implemented with Ratpack</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li><a href="https://twitter.com/gvmtool/status/433731764134363136">Griffon 1.5</a> available through GVM</li>
<li>GVM now offers <a href="https://twitter.com/gvmtool/status/433280770166509568">Gradle 1.11</a> too</li>
<li>Johannes Link, contributor of the @TailRecursive transformation for Groovy 2.3, compared the performance of @TailRecursive on methods vs trampoline() on closures, and indicate that the <a href="https://twitter.com/johanneslink/status/434057275146522625">performance advantage is for @TailRecursive</a></li>
<li>Dave Syer shows an example of a <a href="https://twitter.com/david_syer/status/365367862170353667">Spring Boot controller written in Groovy that outputs JSON</a></li>
<li>Bobby Warner notes that the <a href="https://twitter.com/bobbywarner/status/433277222476980224">Asset pipeline plugin replaces the resources plugin</a> by default from Grails 2.4 onward</li>
</ul>
<h2 id="other-news">Other news</h2>
<ul>
<li>The <a href="https://github.com/groovy/groovy-core/pull/315">@TailRecursive transformation</a> has been contributed by Johannes Link and Cédric Champeau merged the pull request into the Groovy 2.3 branch</li>
<li>Jacob Aae Mikkelsen&rsquo;s Week #7 of the <a href="http://grydeske.net/news/show/30">Grails diary</a></li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>The final version of the <a href="http://www.manning.com/muschko/">Gradle in Action</a> eBook is available</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="http://gr8conf.eu/">Registration for GR8Conf Europe 2014 is open</a></li>
<li>You can find the <a href="http://lanyrd.com/2014/greach/">Greach conference on Lanyard</a></li>
<li>A new <a href="http://www.meetup.com/Warszawa-Java-User-Group-Warszawa-JUG/events/166258012/?utm_content=buffer5ada7&amp;utm_medium=social&amp;utm_source=twitter.com&amp;utm_campaign=buffer">Groovy user group in Warsaw</a>, Poland</li>
<li>Peter Ledbrook is speaking about why your build matters, and the <a href="https://skillsmatter.com/meetups/6202-why-does-your-build-matter">importance of Gradle</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Benefit from Groovy now: when, how, why</title><link>https://glaforge.dev/talks/2014/02/11/benefit-from-groovy-now-when-how-why/</link><pubDate>Tue, 11 Feb 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/talks/2014/02/11/benefit-from-groovy-now-when-how-why/</guid><description>&lt;p>Last week, I was in Stockholm, Sweden, for the JFokus conference. &lt;a href="https://twitter.com/matkar">Mattias Karlsson&lt;/a> had invited me to speak about how to benefit from Groovy, how / when / why developers, projects, companies are using and adopting Groovy in their everyday life.&lt;/p>
&lt;p>So I crafted a brand new presentation illustrating those usage patterns, how they are applied, also giving some examples of existing projects or companies benefiting from Groovy in that way.&lt;/p></description><content:encoded>
<![CDATA[<p>Last week, I was in Stockholm, Sweden, for the JFokus conference. <a href="https://twitter.com/matkar">Mattias Karlsson</a> had invited me to speak about how to benefit from Groovy, how / when / why developers, projects, companies are using and adopting Groovy in their everyday life.</p>
<p>So I crafted a brand new presentation illustrating those usage patterns, how they are applied, also giving some examples of existing projects or companies benefiting from Groovy in that way.</p>
<p>I&rsquo;ve published the slides on SpeakerDeck, and I&rsquo;m embedding them below:</p>
<script async class="speakerdeck-embed" data-id="eb56155070930131f1195e173af38d18" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #8</title><link>https://glaforge.dev/posts/2014/02/11/groovy-weekly-8/</link><pubDate>Tue, 11 Feb 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/02/11/groovy-weekly-8/</guid><description>&lt;p>In Stockholm for the JFokus conference, I was happy to meet some of the members of the Groovy community, and had the chance to speak about the &lt;a href="https://speakerdeck.com/glaforge/benefit-from-groovy-now-why-when-how-jfokus-2014">Groovy usage patterns and how companies integrate Groovy&lt;/a>. But while I’m having fun in Sweden (with heaps of fever though), we are laying out the new infrastructure for the Groovy project with the help of JetBrains and JFrog, or work is underway to make Groovy’s JSON support the fastest around (more infor to come soon)! So it’s been a busy week!&lt;/p></description><content:encoded>
<![CDATA[<p>In Stockholm for the JFokus conference, I was happy to meet some of the members of the Groovy community, and had the chance to speak about the <a href="https://speakerdeck.com/glaforge/benefit-from-groovy-now-why-when-how-jfokus-2014">Groovy usage patterns and how companies integrate Groovy</a>. But while I’m having fun in Sweden (with heaps of fever though), we are laying out the new infrastructure for the Groovy project with the help of JetBrains and JFrog, or work is underway to make Groovy’s JSON support the fastest around (more infor to come soon)! So it’s been a busy week!</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://www.gradle.org/docs/1.11/release-notes">Gradle 1.11 release</a> announced by Luke Daley</li>
<li>A Groovy library offering a <a href="https://github.com/johnnywey/flipside">functional Option</a> implementation and a matcher</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>MrHaki is looking into <a href="http://mrhaki.blogspot.fr/2014/02/grails-goodness-customize-root-element.html">customizing root rlement name collections for Grails XML marshalling</a></li>
<li>André Steingreß talks about the ability to <a href="http://blog.andresteingress.com/2014/02/07/grails-environment-in-build-config/">use the Grails Environment class anywhere in BuildConfig.groovy</a> since Grails 2.3</li>
<li>Tamsin Slinn explains how she created her first Groovy DSL, to <a href="http://blog.anorakgirl.co.uk/2014/02/my-first-groovy-dsl/">automate the management of her Jenkins jobs</a></li>
<li>Craig Atkinson on <a href="http://www.objectpartners.com/2014/02/07/groovy-convenience-in-javascript-with-lo-dash/">Groovy convenience in JavaScript</a></li>
<li>Andrew Taylor shows how to <a href="http://www.redtoad.ca/ataylor/2014/02/setting-headers-url-requests-with-groovy/">set request attributes on URL requests</a> in Groovy</li>
<li>Options for <a href="http://www.intelligrape.com/blog/2014/02/06/grails-debugging-in-version-2-3-x-and-above/">Grails debugging in Grails 2.3</a> and beyond</li>
<li>MrHaki shows how to run <a href="http://mrhaki.blogspot.fr/2014/02/grails-goodness-run-forked-tests-in.html">Grails forked tests in IntelliJ IDEA</a></li>
<li>Using Groovy <a href="http://expertalks.wordpress.com/2014/02/10/using-groovy-ast-transformations-for-dsl-manipulation/">AST transformations for DSL manipulation</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Guillaume Laforge presented a talk at JFokus 2014 about the <a href="https://speakerdeck.com/glaforge/benefit-from-groovy-now-why-when-how-jfokus-2014">usage patterns of Groovy</a>, for scripting, testing, extension points, business rules, DSLs, and full-blown apps</li>
<li>In this presentation recorded at SpringOne2GX 2013, Joe Rinehart explains how to integrate <a href="http://www.infoq.com/presentations/grails-twitter-bootstrap">Twitter Bootstrap into a Grails</a> application.</li>
<li><a href="http://java.dzone.com/articles/introduction-groovy-runtime">Marcin Grzejszczak shares a presentation about an introduction to metaprogramming and AST transforms</a></li>
<li><a href="https://twitter.com/franckdepierre/status/433011063693783040">Continuous Delivery with Docker, Gradle and Jenkins</a></li>
</ul>
<h2 id="mailing-list-discussions">Mailing-list discussions</h2>
<ul>
<li>Guillaume Laforge wrote an update on the Groovy mailing-list about some <a href="http://groovy.329449.n5.nabble.com/ANN-Groovy-infrastructure-update-td5718414.html">infrastructure news regarding the Groovy project</a> (CI server, URL, new documentation, snapshot &amp; release publishing, etc)</li>
<li>Guillaume Laforge announces that <a href="http://groovy.329449.n5.nabble.com/Groovy-JSON-Parser-update-23x-speed-improvement-for-JsonSlurper-td5718189i40.html#a5718336">Groovy 2.3 will raise the JDK requirement to JDK 6</a>, compared to all other versions which had still be compatible with JDK 5 so far.</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Cédric Champeau has a prototype of a <a href="https://github.com/melix/groovy-core/blob/69aeea4606b272d4bcce47602cac6c04b8c72498/subprojects/groovy-templates/src/test/groovy/groovy/text/MarkupTemplateEngineTest.groovy">potential new template engine</a> addition for Groovy, which is a markup builder based template engine, which also understands includes</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Guillaume Laforge receives his second &ldquo;<a href="https://twitter.com/glaforge/status/433158041643810816">JavaOne Rock Star</a>&rdquo; award, for his participation to the JavaOne 2013 conference, where he spoke about Groovy, and also represented Groovy and won the &ldquo;Script Bowl&rdquo; competition</li>
<li>The <a href="https://twitter.com/jbaruch/status/432903532669190144">Groovy project is overhauling its infrastructure</a> and is going through OSS JFrog.org and JFrog Bintray for uploading its snapshots built with JetBrains TeamCity and soon for the releases too</li>
<li>With the new CI server sponsored by JetBrains, the Groovy project also has a build plan for <a href="https://twitter.com/cedricchampeau/status/433258782177906688">building Groovy against the future JDK 9</a> releases</li>
<li>Johannes Link shares a download of his <a href="https://twitter.com/johanneslink/status/431432766404976640">ongoing @TailRecursive transform</a> to be included in Groovy 2.3</li>
<li>Magnus Rundberget is experimenting with a <a href="https://twitter.com/mrundberget/status/432542766476713984">Groovy plugin for the LightTable IDE</a></li>
<li>Dan Woods published a <a href="https://twitter.com/danveloper/status/430692110560272384">new plugin for Grails</a> for simplifying &amp; streamlining the implementation of RESTful renderers</li>
<li>Burt Beckwith was noticing that <a href="https://twitter.com/burtbeckwith/status/432638090373369857">older editions of the GR8Conf conference series are available online</a>, if you&rsquo;re interested in watching past presentations and look at slide decks</li>
<li>Groovy comes with its own <a href="https://twitter.com/dailygrailstip/status/432761726283440128">permutation generator</a> used by the permutations() method on collections</li>
</ul>
<h2 id="other-news">Other news</h2>
<ul>
<li>Latest <a href="http://grydeske.net/news/show/29">Grails Diary</a> by Jacob Aae Mikkelsen</li>
<li>Tim Yates is <a href="http://timyates.github.io/groovy-stream/main.html">documenting his Groovy stream library</a> with Asciidoctor and JBake</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>The authors of the &ldquo;Groovy 2 Cookbook&rdquo; book are launching a little <a href="http://groovy.aestasit.com/contest.html">contest for a giveaway of the book</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>The Greach conference in Madrid <a href="http://greach.es/">refreshed its website</a> look and published the full agenda</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #7</title><link>https://glaforge.dev/posts/2014/02/04/groovy-weekly-7/</link><pubDate>Tue, 04 Feb 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/02/04/groovy-weekly-7/</guid><description>&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://www.ratpack.io/versions/0.9.1">Ratpack 0.9.1 is released&lt;/a>, with performance and memory consumption improvements, as well as an RxJava module&lt;/li>
&lt;li>John Engelman releases &lt;a href="http://imperceptiblethoughts.com/post/74739966407/grails-gradle-plugin-2-0-0-released">version 2.0.0 of the Grails-Gradle plugin&lt;/a>&lt;/li>
&lt;li>Cédric Champeau created a &lt;a href="https://github.com/melix/jbake-gradle-plugin">Gradle plugin for JBake&lt;/a>, to be able to render static web sites as part of your Gradle build&lt;/li>
&lt;li>Release of an &lt;a href="http://grails.org/plugin/api-toolkit">update to the Grails Api Toolkit plugin&lt;/a> helping build API with REST, RPC and HATEOAS support&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>The &lt;a href="http://groovy.codehaus.org/Groovy+style+and+language+feature+guidelines+for+Java+developers">Groovy style guide&lt;/a> shared by Guillaume Laforge echoing a &lt;a href="https://twitter.com/pcarey/status/428676607109111808">conversation&lt;/a> on Twitter about the bad practice of putting def everywhere&lt;/li>
&lt;li>Cédric Champeau details how to &lt;a href="http://melix.github.io/blog//2014/02/hosting-jbake-github.html">create your own static blog with JBake, Gradle and Github&lt;/a>&lt;/li>
&lt;li>An article on &lt;a href="http://www.objectpartners.com/2014/01/28/memoization-in-groovy">Groovy Memoization&lt;/a> by Brendon Anderson&lt;/li>
&lt;li>&lt;a href="http://dmahapatro.blogspot.fr/2014/01/how-is-groovy-groovy.html">Why is Groovy groovy&lt;/a>?&lt;/li>
&lt;li>Burt Beckwith offers &lt;a href="http://burtbeckwith.com/blog/?p=2029">one more approach for diagnosing spring-security-core login errors&lt;/a>&lt;/li>
&lt;li>André Steingreß &lt;a href="http://blog.andresteingress.com/2014/01/31/grails-audit-logging/">covers the Grails Audit plugin&lt;/a>&lt;/li>
&lt;li>André Steingreß is covering &lt;a href="http://blog.andresteingress.com/2014/01/28/functional-testing-with-cucumber/">Grails testing with Cucumber&lt;/a>&lt;/li>
&lt;li>Following up on his previous post, André Steingreß &lt;a href="http://blog.andresteingress.com/2014/01/29/functional-testing-with-cucumber-followup/">continues his Grails testing with Cucumber&lt;/a>&lt;/li>
&lt;li>Jochen Theodorou explains &lt;a href="http://blackdragsview.blogspot.fr/2014/01/what-class-duplication-is-and-how-it.html">what class duplication is&lt;/a> and how it happens&lt;/li>
&lt;li>Testing &lt;a href="http://softnoise.wordpress.com/2014/01/27/grails-cucumber-with-httpbuilder-remotecontrol/">Grails with Cucumber, HTTPBuilder, and RemoteControl&lt;/a>&lt;/li>
&lt;li>Ken Kousen plays with &lt;a href="http://kousenit.wordpress.com/2014/02/02/groovy-groundhogs-revisited/">Groovy&amp;rsquo;s date support&lt;/a>&lt;/li>
&lt;li>Hubert Klein Ikkink shares some tips about &lt;a href="http://mrhaki.blogspot.fr/2014/02/grails-goodness-cleaning-up.html">cleaning up your Grails projects&lt;/a>&lt;/li>
&lt;li>Following up on MrHaki&amp;rsquo;s Grails clean-up tip, André Steingreß offers his own &lt;a href="http://blog.andresteingress.com/2014/02/03/grails-clean/">Grails tip for cleaning up everything&lt;/a> in a single rm command&lt;/li>
&lt;li>Michael Scharhag blogs about &lt;a href="http://www.mscharhag.com/2014/01/using-database-views-in-grails.html">using database views in Grails&lt;/a>&lt;/li>
&lt;li>A tutorial on developing a &lt;a href="http://developer.cloudbees.com/bin/view/DEV/GrailsAppFacebookConnect">Grails application with Facebook Connect&lt;/a>&lt;/li>
&lt;li>A &lt;a href="http://sigma-infosolutions.blogspot.in/2014/01/a-broad-stroke-overview-of-grails-web.html">broad stroke overview of Grails&lt;/a> web app development framework&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>Hubert Klein Ikkink, alias MrHaki, &lt;a href="http://mrhaki.blogspot.fr/2014/02/getting-groovy-g8conf-2013-europe-on.html">presented Getting Groovy&lt;/a>, at GR8Conf Europe 2013.&lt;/li>
&lt;li>Ken Kousen advises Java developers &lt;a href="http://www.infoq.com/presentations/java-groovy-2gx">how to do similar tasks in Groovy&lt;/a>: building and testing applications, accessing both relational and NoSQL databases, accessing web services, and more.&lt;/li>
&lt;li>At GR8Conf US 2013, Craig Atkinson presents the advantages of using the &lt;a href="http://www.infoq.com/presentations/grails-geb-functional-test">Geb functional testing&lt;/a> library for creating robust and readable tests with both JUnit and Spock, and configuring Geb for testing across multiple browsers.&lt;/li>
&lt;li>Recorded at GR8Conf US 2013, Kyle Boon reviews &lt;a href="http://www.infoq.com/presentations/rest-groovy-framework">3 frameworks for building RESTful WS&lt;/a> (Grails, Dropwizard and Ratpack), comparing their code readability, maintainability, deployment, metrics collection, scalability and testability.&lt;/li>
&lt;li>At SpringOne2GX 2013, Joe Rinehart discusses some of the &lt;a href="http://www.infoq.com/presentations/grails-security">essential security topics for Grails&lt;/a> (and Java) Web applications, showing how Grails can make life easier and the pitfalls of attempting to secure highly dynamic code.&lt;/li>
&lt;li>Ryan Vanderwerf explains setting up Terracotta and &lt;a href="http://www.infoq.com/presentations/grails-terracotta-quartz">clustering a Grails application&lt;/a> using Ehcache, HTTP Session in Tomcat, and Quartz. Recorded at GR8Conf US 2013.&lt;/li>
&lt;/ul>
&lt;h2 id="mailing-list-discussions">Mailing-list discussions&lt;/h2>
&lt;ul>
&lt;li>Dinko Srkoč announces the birth of his &lt;a href="http://groovy.329449.n5.nabble.com/ann-groovy-sql-Sql-and-streaming-ResultSet-td5718265.html">stream extension to Groovy&amp;rsquo;s Sql&lt;/a> facility (potentially to be included in Groovy)&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippets">Code snippets&lt;/h2>
&lt;ul>
&lt;li>Tim Yates is trying to &lt;a href="https://twitter.com/tim_yates/status/428488170682863616">implement Groovy&amp;rsquo;s collate() method with Java 8 streams&lt;/a> and lambdas.&lt;/li>
&lt;li>Tim Yates offers a little sample showing how to &lt;a href="https://gist.github.com/timyates/8648011">display a little spinner icon in text mode&lt;/a> for long running tasks&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>Cédric Champeau lobbies for having the &lt;a href="https://twitter.com/CedricChampeau/status/430744126955745280">normal &amp;ldquo;groovy&amp;rdquo; JAR to also &amp;ldquo;jarjar&amp;rdquo; the mandatory basic dependencies&lt;/a> of ASM, Antlr and Commons-CLI, like with the Groovy all JAR. You can vote for that JIRA issue.&lt;/li>
&lt;li>Andrés Almiray tweets his &lt;a href="https://twitter.com/aalmiray/status/429222410425212928">desire to see a Gradle portal plugin&lt;/a> soon&lt;/li>
&lt;li>Dierk König tweets about &lt;a href="https://twitter.com/mittie/status/430271085486288896">Groovy being for getting things done&lt;/a> rather than for showing off!&lt;/li>
&lt;li>groovy.io are &lt;a href="https://twitter.com/groovyio/status/430498283547406336">looking for sponsors&lt;/a> for their upcoming hackathon&lt;/li>
&lt;li>The Grain static site generator introduces &lt;a href="https://twitter.com/grainframework/status/428881821502144514">dynamic rendering anywhere&lt;/a>&lt;/li>
&lt;li>Grails provides a command to &lt;a href="https://twitter.com/dailygrailstip/status/429848791668097024">generate a zip file to attach to a JIRA issue&lt;/a> if you need to file a bug report with a reproducible problem&lt;/li>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/430464483589623809">Gradle 1.11-rc1 is available through GVM&lt;/a>&lt;/li>
&lt;li>The Grails force-ssl plugin adds an &lt;a href="https://twitter.com/davydotcom/status/430692627323686913">@SSLRequired&lt;/a> annotation&lt;/li>
&lt;li>Dan Woods published a &lt;a href="https://twitter.com/danveloper/status/430692110560272384">new plugin for Grails for simplifying &amp;amp; streamlining the implementation of RESTful renderers&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="other-news">Other news&lt;/h2>
&lt;ul>
&lt;li>Week 5 of the &lt;a href="http://grydeske.net/news/show/28">Grails diary&lt;/a> by Jacob Aae Mikkelsen&lt;/li>
&lt;/ul>
&lt;h2 id="books">Books&lt;/h2>
&lt;ul>
&lt;li>The &lt;a href="http://groovy.aestasit.com/">Groovy 2 Cookbook has its dedicated website&lt;/a>&lt;/li>
&lt;li>A short &lt;a href="http://shitmores.blogspot.fr/2014/02/book-review-groovy-2-cookbook.html">review of the &amp;ldquo;Groovy 2 cookbook&amp;rdquo;&lt;/a> book&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>SkillsMatter, the organizer of the Groovy / Grails eXchange conference since 2007, have overhauled their website, and you can already see the page for the &lt;a href="https://skillsmatter.com/conferences/1957-groovy-grails-exchange-2014">upcoming GGeX 2014 edition&lt;/a>&lt;/li>
&lt;li>Dan Woods is &lt;a href="http://www.meetup.com/San-Francisco-Grails-Centro/events/162510932/">speaking about Grails 2.3&amp;rsquo;s Rest capabilities&lt;/a> on February 27th at the San Francisco Grails meetup&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://www.ratpack.io/versions/0.9.1">Ratpack 0.9.1 is released</a>, with performance and memory consumption improvements, as well as an RxJava module</li>
<li>John Engelman releases <a href="http://imperceptiblethoughts.com/post/74739966407/grails-gradle-plugin-2-0-0-released">version 2.0.0 of the Grails-Gradle plugin</a></li>
<li>Cédric Champeau created a <a href="https://github.com/melix/jbake-gradle-plugin">Gradle plugin for JBake</a>, to be able to render static web sites as part of your Gradle build</li>
<li>Release of an <a href="http://grails.org/plugin/api-toolkit">update to the Grails Api Toolkit plugin</a> helping build API with REST, RPC and HATEOAS support</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>The <a href="http://groovy.codehaus.org/Groovy+style+and+language+feature+guidelines+for+Java+developers">Groovy style guide</a> shared by Guillaume Laforge echoing a <a href="https://twitter.com/pcarey/status/428676607109111808">conversation</a> on Twitter about the bad practice of putting def everywhere</li>
<li>Cédric Champeau details how to <a href="http://melix.github.io/blog//2014/02/hosting-jbake-github.html">create your own static blog with JBake, Gradle and Github</a></li>
<li>An article on <a href="http://www.objectpartners.com/2014/01/28/memoization-in-groovy">Groovy Memoization</a> by Brendon Anderson</li>
<li><a href="http://dmahapatro.blogspot.fr/2014/01/how-is-groovy-groovy.html">Why is Groovy groovy</a>?</li>
<li>Burt Beckwith offers <a href="http://burtbeckwith.com/blog/?p=2029">one more approach for diagnosing spring-security-core login errors</a></li>
<li>André Steingreß <a href="http://blog.andresteingress.com/2014/01/31/grails-audit-logging/">covers the Grails Audit plugin</a></li>
<li>André Steingreß is covering <a href="http://blog.andresteingress.com/2014/01/28/functional-testing-with-cucumber/">Grails testing with Cucumber</a></li>
<li>Following up on his previous post, André Steingreß <a href="http://blog.andresteingress.com/2014/01/29/functional-testing-with-cucumber-followup/">continues his Grails testing with Cucumber</a></li>
<li>Jochen Theodorou explains <a href="http://blackdragsview.blogspot.fr/2014/01/what-class-duplication-is-and-how-it.html">what class duplication is</a> and how it happens</li>
<li>Testing <a href="http://softnoise.wordpress.com/2014/01/27/grails-cucumber-with-httpbuilder-remotecontrol/">Grails with Cucumber, HTTPBuilder, and RemoteControl</a></li>
<li>Ken Kousen plays with <a href="http://kousenit.wordpress.com/2014/02/02/groovy-groundhogs-revisited/">Groovy&rsquo;s date support</a></li>
<li>Hubert Klein Ikkink shares some tips about <a href="http://mrhaki.blogspot.fr/2014/02/grails-goodness-cleaning-up.html">cleaning up your Grails projects</a></li>
<li>Following up on MrHaki&rsquo;s Grails clean-up tip, André Steingreß offers his own <a href="http://blog.andresteingress.com/2014/02/03/grails-clean/">Grails tip for cleaning up everything</a> in a single rm command</li>
<li>Michael Scharhag blogs about <a href="http://www.mscharhag.com/2014/01/using-database-views-in-grails.html">using database views in Grails</a></li>
<li>A tutorial on developing a <a href="http://developer.cloudbees.com/bin/view/DEV/GrailsAppFacebookConnect">Grails application with Facebook Connect</a></li>
<li>A <a href="http://sigma-infosolutions.blogspot.in/2014/01/a-broad-stroke-overview-of-grails-web.html">broad stroke overview of Grails</a> web app development framework</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Hubert Klein Ikkink, alias MrHaki, <a href="http://mrhaki.blogspot.fr/2014/02/getting-groovy-g8conf-2013-europe-on.html">presented Getting Groovy</a>, at GR8Conf Europe 2013.</li>
<li>Ken Kousen advises Java developers <a href="http://www.infoq.com/presentations/java-groovy-2gx">how to do similar tasks in Groovy</a>: building and testing applications, accessing both relational and NoSQL databases, accessing web services, and more.</li>
<li>At GR8Conf US 2013, Craig Atkinson presents the advantages of using the <a href="http://www.infoq.com/presentations/grails-geb-functional-test">Geb functional testing</a> library for creating robust and readable tests with both JUnit and Spock, and configuring Geb for testing across multiple browsers.</li>
<li>Recorded at GR8Conf US 2013, Kyle Boon reviews <a href="http://www.infoq.com/presentations/rest-groovy-framework">3 frameworks for building RESTful WS</a> (Grails, Dropwizard and Ratpack), comparing their code readability, maintainability, deployment, metrics collection, scalability and testability.</li>
<li>At SpringOne2GX 2013, Joe Rinehart discusses some of the <a href="http://www.infoq.com/presentations/grails-security">essential security topics for Grails</a> (and Java) Web applications, showing how Grails can make life easier and the pitfalls of attempting to secure highly dynamic code.</li>
<li>Ryan Vanderwerf explains setting up Terracotta and <a href="http://www.infoq.com/presentations/grails-terracotta-quartz">clustering a Grails application</a> using Ehcache, HTTP Session in Tomcat, and Quartz. Recorded at GR8Conf US 2013.</li>
</ul>
<h2 id="mailing-list-discussions">Mailing-list discussions</h2>
<ul>
<li>Dinko Srkoč announces the birth of his <a href="http://groovy.329449.n5.nabble.com/ann-groovy-sql-Sql-and-streaming-ResultSet-td5718265.html">stream extension to Groovy&rsquo;s Sql</a> facility (potentially to be included in Groovy)</li>
</ul>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li>Tim Yates is trying to <a href="https://twitter.com/tim_yates/status/428488170682863616">implement Groovy&rsquo;s collate() method with Java 8 streams</a> and lambdas.</li>
<li>Tim Yates offers a little sample showing how to <a href="https://gist.github.com/timyates/8648011">display a little spinner icon in text mode</a> for long running tasks</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Cédric Champeau lobbies for having the <a href="https://twitter.com/CedricChampeau/status/430744126955745280">normal &ldquo;groovy&rdquo; JAR to also &ldquo;jarjar&rdquo; the mandatory basic dependencies</a> of ASM, Antlr and Commons-CLI, like with the Groovy all JAR. You can vote for that JIRA issue.</li>
<li>Andrés Almiray tweets his <a href="https://twitter.com/aalmiray/status/429222410425212928">desire to see a Gradle portal plugin</a> soon</li>
<li>Dierk König tweets about <a href="https://twitter.com/mittie/status/430271085486288896">Groovy being for getting things done</a> rather than for showing off!</li>
<li>groovy.io are <a href="https://twitter.com/groovyio/status/430498283547406336">looking for sponsors</a> for their upcoming hackathon</li>
<li>The Grain static site generator introduces <a href="https://twitter.com/grainframework/status/428881821502144514">dynamic rendering anywhere</a></li>
<li>Grails provides a command to <a href="https://twitter.com/dailygrailstip/status/429848791668097024">generate a zip file to attach to a JIRA issue</a> if you need to file a bug report with a reproducible problem</li>
<li><a href="https://twitter.com/gvmtool/status/430464483589623809">Gradle 1.11-rc1 is available through GVM</a></li>
<li>The Grails force-ssl plugin adds an <a href="https://twitter.com/davydotcom/status/430692627323686913">@SSLRequired</a> annotation</li>
<li>Dan Woods published a <a href="https://twitter.com/danveloper/status/430692110560272384">new plugin for Grails for simplifying &amp; streamlining the implementation of RESTful renderers</a></li>
</ul>
<h2 id="other-news">Other news</h2>
<ul>
<li>Week 5 of the <a href="http://grydeske.net/news/show/28">Grails diary</a> by Jacob Aae Mikkelsen</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>The <a href="http://groovy.aestasit.com/">Groovy 2 Cookbook has its dedicated website</a></li>
<li>A short <a href="http://shitmores.blogspot.fr/2014/02/book-review-groovy-2-cookbook.html">review of the &ldquo;Groovy 2 cookbook&rdquo;</a> book</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>SkillsMatter, the organizer of the Groovy / Grails eXchange conference since 2007, have overhauled their website, and you can already see the page for the <a href="https://skillsmatter.com/conferences/1957-groovy-grails-exchange-2014">upcoming GGeX 2014 edition</a></li>
<li>Dan Woods is <a href="http://www.meetup.com/San-Francisco-Grails-Centro/events/162510932/">speaking about Grails 2.3&rsquo;s Rest capabilities</a> on February 27th at the San Francisco Grails meetup</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #6</title><link>https://glaforge.dev/posts/2014/01/28/groovy-weekly-6/</link><pubDate>Tue, 28 Jan 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/01/28/groovy-weekly-6/</guid><description>&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>The first &lt;a href="https://spring.io/blog/2014/01/22/spring-boot-1-0-0-rc1-released">release candidate of Spring Boot&lt;/a> is out, including its lean Groovy support&lt;/li>
&lt;li>Grain releases are now &lt;a href="https://twitter.com/grainframework/status/425887785312976897">available in Maven Central&lt;/a>&lt;/li>
&lt;li>Andrés Almiray released &lt;a href="https://twitter.com/aalmiray/status/426431315463110656">version 0.3.0 of his Gipsy&lt;/a> AST transformation for simplifying usage of the service provider interface system&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>Pivotal posted Guillaume Laforge&amp;rsquo;s article on the &lt;a href="http://blog.gopivotal.com/products/3-million-downloads-of-the-groovy-programming-language-in-2013">3 million downloads of Groovy&lt;/a> last year and his analysis&lt;/li>
&lt;li>Kyle Boon describes &lt;a href="http://kyleboon.org/blog/2014/01/22/using-meta-annotations-to-dry-your-groovy/">how to use meta-annotations to reduce boilerplate code&lt;/a>&lt;/li>
&lt;li>GVM, the Groovy enVironment Manager, is featured on the methods and tools website, thanks to an &lt;a href="http://www.methodsandtools.com/tools/gvm.php">article&lt;/a> from Marco Vermeulen&lt;/li>
&lt;li>At SpringOne2GX 2013, Ryan Vanderwerf explains &lt;a href="http://www.infoq.com/presentations/grails-aws-vpc-2gx">how to create and deploy a Grails application on AWS VPC&lt;/a> using various services such as RDS, S3, autoscaling, S3FS, EBS, etc.&lt;/li>
&lt;li>An infographic from Cygnet Infotech about &lt;a href="http://www.cygnet-infotech.com/infographic-6-reasons-why-grails-is-an-awesome-java-web-framework">6 reasons why Grails is an awesome&lt;/a> Java Web framework&lt;/li>
&lt;li>Bert Van Den Brande writes about using &lt;a href="http://blog.bertvdbrande.com/java/spring-boot-applied-springmvcgroovy/">Spring Boot with Groovy&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>ZeroTurnaround published a decision maker’s &lt;a href="http://zeroturnaround.com/rebellabs/java-build-tools-part-2-a-decision-makers-comparison-of-maven-gradle-and-ant-ivy/">comparison of Maven, Gradle and Ant + Ivy&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.objectpartners.com/2014/01/24/simpler-stored-procedures-with-groovy/">Simpler stored procedures with Groovy&lt;/a> by Jeff Sheets from Object Partners&lt;/li>
&lt;li>Burt Beckwith explains the general process of &lt;a href="http://www.infoq.com/presentations/grails-plugin-2gx">creating a Grails plug-in&lt;/a>, the different types of plug-ins, and how to deploy, release and test them.&lt;/li>
&lt;li>Will Buck provides advice for those interested to &lt;a href="http://www.infoq.com/presentations/grails-upgrade">upgrade their applications from Grails 1.x to 2.x&lt;/a>&lt;/li>
&lt;li>Venkat Subramaniam shows how to &lt;a href="http://www.infoq.com/presentations/groovy-design-patterns">implement several design patterns&lt;/a>, including some GoF ones, using Groovy’s dynamic nature and closures.&lt;/li>
&lt;li>Jim Driscoll shows how to create Napili, a small program accepting user scripts to &lt;a href="http://www.infoq.com/presentations/turtle-groovy-dsl">control a turtle using GroovyShell&lt;/a>, the Binding, overriding behavior with set/getVariable and invokeMethod, and more, recorded at GR8Conf US 2013&lt;/li>
&lt;/ul>
&lt;h2 id="mailing-list-discussions">Mailing-list discussions&lt;/h2>
&lt;ul>
&lt;li>Lari Otari from the Grails team is calling for &lt;a href="http://grails.1312388.n4.nabble.com/Grails-Unit-testing-improvements-for-Grails-2-4-call-for-feedback-td4653480.html">feedback on areas where the Grails unit testing support&lt;/a> could be improved&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>Surge in &lt;a href="https://twitter.com/rebellabs/status/425676447588818944">popularity for Gradle&lt;/a> in the ZeroTurnaround analysis&lt;/li>
&lt;li>Cédric Champeau succeeded &lt;a href="https://twitter.com/cedricchampeau/status/425965163804901376">making Groovy build with JDK 8&lt;/a>, a first step forward to adding further Java 8 support in Groovy&lt;/li>
&lt;li>Johannes Link is &lt;a href="https://twitter.com/johanneslink/status/427833828229779456">reworking his @TailRecursive AST transformation for integration in Groovy 2.3&lt;/a>&lt;/li>
&lt;li>A funny &lt;a href="https://twitter.com/yeiiowsnow/status/425317814388289536">GORM-like tweet&lt;/a> to encourage people to order the second edition of the Grails quick-start guide book&lt;/li>
&lt;/ul>
&lt;h2 id="jobs">Jobs&lt;/h2>
&lt;ul>
&lt;li>A &lt;a href="http://findgrailsjobs.com/job/475-grails-engineer-wixab-telework">remote Grails engineer&lt;/a> for Wixab&lt;/li>
&lt;/ul>
&lt;h2 id="other-news">Other news&lt;/h2>
&lt;ul>
&lt;li>As usual, the the &lt;a href="http://grydeske.net/news/show/27">Grails Diary&lt;/a> is out, week 5 of 2014, by Jacob Aae Mikkelsen&lt;/li>
&lt;li>A new look for the &lt;a href="http://gr8conf.eu/">GR8Conf Europe website&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="books">Books&lt;/h2>
&lt;ul>
&lt;li>The &lt;a href="https://twitter.com/manningmeap/status/425672935576453120">final chapters of Grails in Action&lt;/a>, 2nd edition are now in Manning&amp;rsquo;s Early Access Program. Peter Ledbrook tells us issues can be reported in their GitHub issue tracker (&lt;a href="https://github.com/GrailsInAction/graina2/issues">https://github.com/GrailsInAction/graina2/issues&lt;/a>)&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/gr8ladies/status/426078450236260352">First meetup of the GR8 Ladies&lt;/a> of Minneapolis on January 29th&lt;/li>
&lt;li>The &lt;a href="http://cfp.gr8conf.org/login/auth">Call for Papers&lt;/a> for the GR8Conf Europe (Copenhagen, Denmark, on June 2nd-4th 2014) and GR8Conf US (Minneapolis, USA, on July 28th-29th 2014) conferences is now open&lt;/li>
&lt;li>The Call for Papers for the &lt;a href="http://greach.es/">Greach&lt;/a> conference (Madrid, Spain, on March 28th and 29th 2014) is also open, till January 31st&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<h2 id="releases">Releases</h2>
<ul>
<li>The first <a href="https://spring.io/blog/2014/01/22/spring-boot-1-0-0-rc1-released">release candidate of Spring Boot</a> is out, including its lean Groovy support</li>
<li>Grain releases are now <a href="https://twitter.com/grainframework/status/425887785312976897">available in Maven Central</a></li>
<li>Andrés Almiray released <a href="https://twitter.com/aalmiray/status/426431315463110656">version 0.3.0 of his Gipsy</a> AST transformation for simplifying usage of the service provider interface system</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Pivotal posted Guillaume Laforge&rsquo;s article on the <a href="http://blog.gopivotal.com/products/3-million-downloads-of-the-groovy-programming-language-in-2013">3 million downloads of Groovy</a> last year and his analysis</li>
<li>Kyle Boon describes <a href="http://kyleboon.org/blog/2014/01/22/using-meta-annotations-to-dry-your-groovy/">how to use meta-annotations to reduce boilerplate code</a></li>
<li>GVM, the Groovy enVironment Manager, is featured on the methods and tools website, thanks to an <a href="http://www.methodsandtools.com/tools/gvm.php">article</a> from Marco Vermeulen</li>
<li>At SpringOne2GX 2013, Ryan Vanderwerf explains <a href="http://www.infoq.com/presentations/grails-aws-vpc-2gx">how to create and deploy a Grails application on AWS VPC</a> using various services such as RDS, S3, autoscaling, S3FS, EBS, etc.</li>
<li>An infographic from Cygnet Infotech about <a href="http://www.cygnet-infotech.com/infographic-6-reasons-why-grails-is-an-awesome-java-web-framework">6 reasons why Grails is an awesome</a> Java Web framework</li>
<li>Bert Van Den Brande writes about using <a href="http://blog.bertvdbrande.com/java/spring-boot-applied-springmvcgroovy/">Spring Boot with Groovy</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>ZeroTurnaround published a decision maker’s <a href="http://zeroturnaround.com/rebellabs/java-build-tools-part-2-a-decision-makers-comparison-of-maven-gradle-and-ant-ivy/">comparison of Maven, Gradle and Ant + Ivy</a></li>
<li><a href="http://www.objectpartners.com/2014/01/24/simpler-stored-procedures-with-groovy/">Simpler stored procedures with Groovy</a> by Jeff Sheets from Object Partners</li>
<li>Burt Beckwith explains the general process of <a href="http://www.infoq.com/presentations/grails-plugin-2gx">creating a Grails plug-in</a>, the different types of plug-ins, and how to deploy, release and test them.</li>
<li>Will Buck provides advice for those interested to <a href="http://www.infoq.com/presentations/grails-upgrade">upgrade their applications from Grails 1.x to 2.x</a></li>
<li>Venkat Subramaniam shows how to <a href="http://www.infoq.com/presentations/groovy-design-patterns">implement several design patterns</a>, including some GoF ones, using Groovy’s dynamic nature and closures.</li>
<li>Jim Driscoll shows how to create Napili, a small program accepting user scripts to <a href="http://www.infoq.com/presentations/turtle-groovy-dsl">control a turtle using GroovyShell</a>, the Binding, overriding behavior with set/getVariable and invokeMethod, and more, recorded at GR8Conf US 2013</li>
</ul>
<h2 id="mailing-list-discussions">Mailing-list discussions</h2>
<ul>
<li>Lari Otari from the Grails team is calling for <a href="http://grails.1312388.n4.nabble.com/Grails-Unit-testing-improvements-for-Grails-2-4-call-for-feedback-td4653480.html">feedback on areas where the Grails unit testing support</a> could be improved</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Surge in <a href="https://twitter.com/rebellabs/status/425676447588818944">popularity for Gradle</a> in the ZeroTurnaround analysis</li>
<li>Cédric Champeau succeeded <a href="https://twitter.com/cedricchampeau/status/425965163804901376">making Groovy build with JDK 8</a>, a first step forward to adding further Java 8 support in Groovy</li>
<li>Johannes Link is <a href="https://twitter.com/johanneslink/status/427833828229779456">reworking his @TailRecursive AST transformation for integration in Groovy 2.3</a></li>
<li>A funny <a href="https://twitter.com/yeiiowsnow/status/425317814388289536">GORM-like tweet</a> to encourage people to order the second edition of the Grails quick-start guide book</li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li>A <a href="http://findgrailsjobs.com/job/475-grails-engineer-wixab-telework">remote Grails engineer</a> for Wixab</li>
</ul>
<h2 id="other-news">Other news</h2>
<ul>
<li>As usual, the the <a href="http://grydeske.net/news/show/27">Grails Diary</a> is out, week 5 of 2014, by Jacob Aae Mikkelsen</li>
<li>A new look for the <a href="http://gr8conf.eu/">GR8Conf Europe website</a></li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>The <a href="https://twitter.com/manningmeap/status/425672935576453120">final chapters of Grails in Action</a>, 2nd edition are now in Manning&rsquo;s Early Access Program. Peter Ledbrook tells us issues can be reported in their GitHub issue tracker (<a href="https://github.com/GrailsInAction/graina2/issues">https://github.com/GrailsInAction/graina2/issues</a>)</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="https://twitter.com/gr8ladies/status/426078450236260352">First meetup of the GR8 Ladies</a> of Minneapolis on January 29th</li>
<li>The <a href="http://cfp.gr8conf.org/login/auth">Call for Papers</a> for the GR8Conf Europe (Copenhagen, Denmark, on June 2nd-4th 2014) and GR8Conf US (Minneapolis, USA, on July 28th-29th 2014) conferences is now open</li>
<li>The Call for Papers for the <a href="http://greach.es/">Greach</a> conference (Madrid, Spain, on March 28th and 29th 2014) is also open, till January 31st</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy crosses the 3 million downloads a year mark</title><link>https://glaforge.dev/posts/2014/01/21/groovy-crosses-the-3-million-downloads-a-year-mark/</link><pubDate>Tue, 21 Jan 2014 04:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/01/21/groovy-crosses-the-3-million-downloads-a-year-mark/</guid><description>&lt;p>Back in 2012, I was pretty happy and proud to see Groovy being used more and more, and reaching 1.7 million downloads in 2012. But the competition with other alternative language, I was personally not sure how those figures would evolve over the course of 2013. Fortunately, thanks to the hard work of the Groovy core development team and the friendly community and ecosystem, I&amp;rsquo;m happy to report an impressive ongoing growth for 2013: Groovy crosses the 3 million downloads a year mark!&lt;/p></description><content:encoded>
<![CDATA[<p>Back in 2012, I was pretty happy and proud to see Groovy being used more and more, and reaching 1.7 million downloads in 2012. But the competition with other alternative language, I was personally not sure how those figures would evolve over the course of 2013. Fortunately, thanks to the hard work of the Groovy core development team and the friendly community and ecosystem, I&rsquo;m happy to report an impressive ongoing growth for 2013: Groovy crosses the 3 million downloads a year mark!</p>
<p>I have compiled some statistics thanks to the Maven Central statistics, as well as through slicing and dicing the Codehaus Apache logs, and I&rsquo;d like to illustrate two interesting findings.</p>
<p>First, let&rsquo;s have a look at those download numbers, spanning both 2012 and 2013 to show the trend and growth. In the graph below (sorry for the subtitle in French) I&rsquo;ve stacked up the Codehaus downloads (in blue) and the Maven Central downloads (in green), and put the total on top (in yellow).</p>
<p><a href="/img/misc/groovy+download+evolution.png"><figure>
  <a href="#img-2fa4e552cd75353e94bae9ae0e0f0d98">
    <img src="/img/misc/groovy&#43;download&#43;evolution.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-2fa4e552cd75353e94bae9ae0e0f0d98">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/groovy&#43;download&#43;evolution.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</a></p>
<p>You&rsquo;ll notice that there are more downloads of Groovy &ldquo;as a library&rdquo; than as an installable binary distribution, as Central usually represents 3 to 4 times more downloads than Codehaus, but this is logical as Groovy is just a &ldquo;dependency&rdquo; to add to your project!</p>
<p>The peaks you see on the graph usually correspond to major releases, for example the 2.2.0 RCs and GA releases in October 2013, or 2.1.0 end of December 2012, etc.<br />
The yellow line is showing the general trend in total number of downloads per month, starting from 100K+ per month in the beginning of 2012 up to 300K+ downloads a month at the end of 2013.</p>
<p>Before moving to the second illustration, note that those numbers are downloads of Groovy itself. They don&rsquo;t account for downloads of Grails or Gradle, etc, as those projects actually bundle their own version of Groovy, and thus doesn&rsquo;t add downloads to our stats here. Those figures represent developer and company projects depending on Groovy or developers installing the Groovy distribution on their machines.</p>
<p>Another interesting perspective is to see how the various Groovy versions are being adopted by the community. The graph below illustrates the proportion of each version being downloaded over 2012 and 2013.</p>
<p><a href="/img/misc/groovy+version+adoption.png"><figure>
  <a href="#img-7e30b4b2bd183ac3d1c6cc2e58a92e42">
    <img src="/img/misc/groovy&#43;version&#43;adoption.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-7e30b4b2bd183ac3d1c6cc2e58a92e42">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/groovy&#43;version&#43;adoption.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</a></p>
<p>There have been many beta and RC releases of Groovy 2.0 in 2011 and first half of 2012, and Groovy 1.8 was really going strong, but with the release of Groovy 2.0 in June 2012, we&rsquo;ve had a rapid adoption of Groovy 2.0, even though Groovy 1.8 has still been remaining significant (for instance, some projects like Gradle still rely on Groovy 1.8!).<br />
The release of Groovy 2.1 in January 2013 also induced a cliff of adoption towards the new version, dwarfing 2.0, as Groovy 2.0, 2.1 and 2.2 are very much compatible so users don&rsquo;t hesitate jumping their dependencies up.</p>
<p>And then the release of Groovy 2.2 in November 2013 shows this version is also being nicely adopted, although perhaps less rapidly than 2.1 was &ndash; but remember these are percentages on the graph. And as of December, Groovy 2.2 represents about 40%, slightly above the 38% of Groovy 2.1.</p>
<p>What we see from this graph is that it usually takes around 4 months or so for the next major version of Groovy to be majoritarily adopted by Groovy developers.</p>
<p>With the downloads almost doubling (1.7M to 3.0M) from 2012 to 2013, we see that Groovy clearly continue being very successful and widely used and deployed in tons of projects!<br />
Big thanks to the Groovy core team for its hard work towards this success, and to the whole community and ecosystem for fostering such a growth!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #5</title><link>https://glaforge.dev/posts/2014/01/21/groovy-weekly-5/</link><pubDate>Tue, 21 Jan 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/01/21/groovy-weekly-5/</guid><description>&lt;p>We all know the Groovy community is super active, buzzing with useful projects in the ecosystem, but it’s always interesting to see how our projects evolve in terms of usage. Guillaume Laforge, project lead of Groovy, computed some download statistics for Groovy, and showed &lt;a href="https://glaforge.dev/posts/2014/01/21/groovy-crosses-the-3-million-downloads-a-year-mark/">Groovy almost doubled its downloads, from 1.7 million downloads in 2012 up to 3 million downloads in 2013&lt;/a>! All that, thanks to the hard work of the Groovy core team and the friendly and supportive community.&lt;/p></description><content:encoded>
<![CDATA[<p>We all know the Groovy community is super active, buzzing with useful projects in the ecosystem, but it’s always interesting to see how our projects evolve in terms of usage. Guillaume Laforge, project lead of Groovy, computed some download statistics for Groovy, and showed <a href="https://glaforge.dev/posts/2014/01/21/groovy-crosses-the-3-million-downloads-a-year-mark/">Groovy almost doubled its downloads, from 1.7 million downloads in 2012 up to 3 million downloads in 2013</a>! All that, thanks to the hard work of the Groovy core team and the friendly and supportive community.</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Grails 2.3.5 has been <a href="http://grails.org/2.3.5%20Release%20Notes">released</a></li>
<li>Keegan Witt announces the <a href="https://github.com/groovy/GMavenPlus/wiki/News">GMavenPlus 1.0 release</a>, which offers the best support for building Groovy projects when using Maven</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Guillaume Laforge <a href="https://glaforge.dev/posts/2014/01/21/groovy-crosses-the-3-million-downloads-a-year-mark/">analyses the ongoing growth of Groovy</a> in terms of downloads (3 million downloads in 2013) and illustrates the adoption of the various Groovy version over the past two years, with a couple pretty graphs.</li>
<li>Andrés Almiray explains all the <a href="http://www.jroller.com/aalmiray/entry/griffon_2_0_0_and">details about Griffon 2.0 and beyond</a></li>
<li>Russell Hart on <a href="http://blog.anacoders.com/deploying-ratpack-applications-to-cloudfoundry/">deploying Ratpack applications to Cloud Foundry</a></li>
<li>Mark Perry continues exploring the functional field with Groovy, with <a href="http://mperry.github.io/2014/01/21/folds-and-unfolds.html">folds and unfolds</a></li>
<li>Kunal Dabir shows how to use a <a href="https://github.com/kdabir/lazybone-templates#creating-a-gaelyk-project-using-the-template">Lazybones template for your Gaelyk applications</a></li>
<li><a href="http://sysgears.com/grain/news/deployment-to-github-pages-is-introduced/">Smooth integration with Github Pages is introduced in Grain</a>, the static website generator for Groovy</li>
<li>Julian Exenberger tries to <a href="http://java.dzone.com/articles/builder-pattern-using-java-8-0">replicate Groovy&rsquo;s builders using Java 8 and lambdas</a></li>
<li>David Estes explains the <a href="http://www.redwindsw.com/blog/2014-01-15-moving-from-rails-to-grails-differences-and-similarities">differences and similarities of moving from Rails to Grails</a></li>
<li>Corinne Krych <a href="http://corinnekrych.blogspot.fr/2014/01/secure-your-runtime-groovy-hackengarten.html">reports on the recent Groovy hackergarten</a> that took place in the south of France with Cédric Champeau, on the topic of a secure runtime AST customizer</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>Graeme Rocher talks through the latest <a href="http://www.infoq.com/presentations/grails-rest-async">Async features offered by Grails</a> and how they can be used to create non-blocking REST APIs</li>
<li>Marco Vermeulen showcases using <a href="http://www.infoq.com/presentations/gvm-bdd-cucumber">BDD and Cucumber to develop GVM</a>, an open source tool for managing parallel SDK versions, during GR8Conf US 2013</li>
<li>Jeff Beck shows how to use Codenarc, Cobertura, JSLint, and other tools to perform <a href="http://www.infoq.com/presentations/grails-static-analysis-tools">static analysis on Grails application</a>, recorded at GR8Conf US 2013</li>
<li>Rob Fletcher explains how to <a href="http://www.infoq.com/presentations/grails-vertx-websockets">use Vert.x, WebSockets, continuous unit testing and headless end-to-end testing</a> to create one-page applications in Grails, during GR8Conf US 2013</li>
</ul>
<h2 id="mailing-list-discussions">Mailing-list discussions</h2>
<ul>
<li>Jochen Theodorou gives some <a href="http://groovy.329449.n5.nabble.com/Installing-a-property-through-metaclass-sometimes-does-not-work-td5718106.html#a5718134">high-level explanations of what the future Meta-Object Protocol v2</a> will look like in Groovy 3.0</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li><a href="https://twitter.com/glaforge/status/425299880991801344">Groovy crossed the 3 million downloads mark for 2013</a>, to compare with the 1.7M downloads for 2012, that&rsquo;s quite a growth!</li>
<li>Cédric Champeau merged the <a href="https://twitter.com/cedricchampeau/status/423506229923962880">Groovy closure parameter type inference into master</a></li>
<li>Cédric Champeau is <a href="https://twitter.com/cedricchampeau/status/423853949821939712">setting up the new TeamCity Continuous Integration server</a> for building Groovy, sponsored by JetBrains</li>
<li>Grails 2.3.5 is also <a href="https://twitter.com/gvmtool/status/423896818423189504">available in GVM</a></li>
<li>Some details on the upcoming <a href="https://twitter.com/ratpackweb/status/423597717420769280">RxJava support in Ratpack 0.9.1</a></li>
<li>Andrés Almiray crafts various <a href="https://twitter.com/aalmiray/status/424981529878478848">Lazybones templates for Griffon</a></li>
<li>The Grain static website generator is <a href="https://twitter.com/grainframework/status/425260426851409920">adding Asciidoctor support</a> in the next version</li>
<li>David Dawson shares a picture of <a href="https://twitter.com/davidthecoder/status/423855749165776896/photo/1">Groovy post-it notes</a>!</li>
<li>Looks like Minneapolis is always super active in the Groovy ecosystem, and a &ldquo;<a href="https://twitter.com/gr8ladies">GR8 Ladies</a>&rdquo; initiative is seeing the light of day. You might want to follow their twitter account as the story unfolds.</li>
<li>Rob Fletcher is a fan of <a href="https://twitter.com/rfletcherew/status/425340799820496896">Groovy 2.2&rsquo;s implicit closure coercion</a></li>
<li>Dan Woods is <a href="https://twitter.com/danveloper/status/425343533721989120">joining the Cloud Infrastructure Tools team at Netflix</a></li>
</ul>
<h2 id="podcast">Podcast</h2>
<ul>
<li>For those speaking French, Stéphane Maldini answers Guillaume Laforge&rsquo;s questions on <a href="https://twitter.com/lescastcodeurs/status/425366618525663232">Grails and Reactor on this interview</a> of the French podcast &ldquo;Les Cast Codeurs&rdquo;</li>
</ul>
<h2 id="google-post">Google+ post</h2>
<ul>
<li>Colling Harrington <a href="https://plus.google.com/u/0/+ColinHarrington/posts/K29FVW56AzJ?cfem=1">mentions</a> Aaron Hanson&rsquo;s work on a <a href="https://github.com/simplenotions/simple-http-server">SimpleHTTPServer script</a> in Groovy, in the same vein as Ratpack</li>
</ul>
<h2 id="code-snippet">Code snippet</h2>
<ul>
<li>A Groovy script that takes a <a href="https://gist.github.com/crazy4groovy/8438827#file-csv2list-groovy">CSV file and turns it into a List of Maps</a> using the GroovyCSV library</li>
</ul>
<h2 id="contributions">Contributions</h2>
<ul>
<li>Cédric Champeau joined the Riviera GUG which was organizing a Groovy hackathon last week-end, and they worked and contributed to <a href="http://jira.codehaus.org/browse/GROOVY-6527">GROOVY-6527</a>, a secure runtime AST customizer, and made <a href="https://github.com/rivieragug/groovy-core/commits/secureruntime">several commits that will be contributed to the Groovy project</a></li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li>Dan Woods tells us about startup <a href="http://www.exactuals.com/">Exactuals</a> which is <a href="https://twitter.com/danveloper/status/425524216201347073">hiring a Senior Grails Developer</a> for 
  <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>100</mn><mi>k</mi><mo>−</mo></mrow><annotation encoding="application/x-tex">100k-</annotation></semantics></math></span>

120k per year to work with cloud, and messaging technologies</li>
</ul>
<h2 id="other-news">Other news</h2>
<ul>
<li>As usual, the the <a href="http://grydeske.net/news/show/26">Grails Diary</a> is out, week 4 of 2014, by Jacob Aae Mikkelsen</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>A <a href="http://www.amazon.com/review/R395UVGPILFN68/ref=cm_cr_pr_perm?ie=UTF8&amp;ASIN=1935182943&amp;linkCode=&amp;nodeID=&amp;tag=">review of Ken Kousen&rsquo;s Making Java Groovy book</a> on Amazon</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>A <a href="http://www.pcquest.com/pcquest/news/206418/intelligrape-software-launches-indian-chapter-world-s-largest-grail-conference">report on the first Grails Conf India conference</a> organized by IntelliGrape</li>
<li>The <a href="http://cfp.gr8conf.org/login/auth">Call for Papers</a> for the GR8Conf Europe (Copenhagen, Denmark, on June 2nd-4th 2014) and GR8Conf US (Minneapolis, USA, on July 28th-29th 2014) conferences is now open</li>
<li>The Call for Papers for the <a href="http://greach.es/">Greach</a> conference (Madrid, Spain, on March 28th and 29th 2014) is also open, till January 31st</li>
</ul>
<p>Don&rsquo;t forget you can <a href="http://bit.ly/groovyweekly">contribute to this weekly column</a> of Groovy news, and that you can <a href="http://bit.ly/groovy-weekly-subscribe">subscribe through a newsletter</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #4</title><link>https://glaforge.dev/posts/2014/01/14/groovy-weekly-4/</link><pubDate>Tue, 14 Jan 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/01/14/groovy-weekly-4/</guid><description>&lt;p>Here’s the fourth edition of the Groovy Weekly column! The holidays are gone, and tons of news are here for your consumption.&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>Jim Northrop &lt;a href="https://groups.google.com/forum/#!topic/caelyf/RAEXqBpDsSA">released Caelyf 1.1.2&lt;/a>, the lightweight Groovy web toolkit for Cloud Foundry&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>Guillaume Laforge wrote a small tutorial on &lt;a href="https://github.com/glaforge/ratpack-demo-cloudfoundry/blob/master/README.md">how to deploy a Ratpack application to Cloud Foundry&lt;/a>, with a dedicated buildpack developed by Ben Hale&lt;/li>
&lt;li>Cédric Champeau blogs about the upcoming &lt;a href="http://melix.github.io/blog/2014/01/closure_param_inference.html">closure parameter type inference&lt;/a> for Groovy&lt;/li>
&lt;li>Guillaume Laforge shares the &lt;a href="http://bit.ly/gdc9notes">notes of the last Groovy developer meeting&lt;/a> in London prior to the Groovy Grails eXchance conference, covering various topics about the roadmap of Groovy 2.3 and Groovy 3.0.&lt;/li>
&lt;li>Peter Niederwieser authored a document explaining &lt;a href="https://github.com/spockframework/spock/blob/groovy-1.8/CONTRIBUTING.md">how to contribute to the development of the Spock&lt;/a> testing framework&lt;/li>
&lt;li>Ken Kousen uses &lt;a href="https://weblogs.java.net/blog/manningpubs/archive/2014/01/07/groovy-weather-pogos-gson-and-open-weather-ken-kousen-making-java-groovy-45-savings">Groovy, POGOs, GSON and the Open Weather API&lt;/a>&lt;/li>
&lt;li>Dustin Marx blogs about &lt;a href="http://java.dzone.com/articles/identifying-gradle-conventions">identifying Gradle conventions&lt;/a>&lt;/li>
&lt;li>Noam Tenne explains how to &lt;a href="http://blog.10ne.org/2014/01/08/registering-new-spring-beans-in-grails-during-runtime/">register new Spring beans in Grails at runtime&lt;/a>&lt;/li>
&lt;li>New Relic, the app performance management solution, adds &lt;a href="http://blog.newrelic.com/2014/01/08/new-relic-supports-grails/">Groovy and Grails support to its Java agent&lt;/a>&lt;/li>
&lt;li>There&amp;rsquo;s now a &lt;a href="http://blog.testfairy.com/testfairy-gradle-plugin/">TestFairy plugin for Gradle&lt;/a>, to test Android application with the &lt;a href="http://www.testfairy.com/">TestFairy&lt;/a> platform&lt;/li>
&lt;li>Amit Jain from IntelliGrape, organizers of the GrailsConf India conference, is listing &lt;a href="http://www.indicthreads.com/10774/15-reasons-grails-web-application-framework/">15 reasons why developers should use the Grails&lt;/a> web application framework&lt;/li>
&lt;li>Amit Jain from IntelliGrape is writing about &lt;a href="http://www.intelligrape.com/blog/2014/01/10/spock-confinemetaclasschanges-annotation-made-writing-grails-unit-test-easier/">Spock&amp;rsquo;s @ConfineMetaClassChanges annotation&lt;/a> to make writing grails unit test easier&lt;/li>
&lt;li>The &lt;a href="http://www.itexto.com.br/devkico/en/?p=47">social value of Groovy and Grails in Brazil&lt;/a> by Henrique Lobo Weissmann&lt;/li>
&lt;li>Dustin Marx explains how to &lt;a href="http://www.javacodegeeks.com/2014/01/simple-gradle-java-plugin-customization.html">customize the Gradle Java plugin&lt;/a>&lt;/li>
&lt;li>Stergios Papadimitriou shows that &lt;a href="https://code.google.com/p/jlabgroovy/wiki/JavaFFTvsNative">Fast Fourier Transforms can be performed in GroovyLab faster than plain C&lt;/a>, and as fast as optimized C routines&lt;/li>
&lt;li>Peter Ledbrook &lt;a href="http://grails.org/plugin/searchable">updated the Grails Searchable plugin installation instructions&lt;/a> to show how to make it work with Grails 2.3&lt;/li>
&lt;li>Ondrej Kvasnovsky blogs about &lt;a href="https://vaadin.com/blog/-/blogs/grails-integration-with-vaadin">Grails integration with Vaadin&lt;/a>&lt;/li>
&lt;li>A &lt;a href="http://java.dzone.com/articles/closures-groovy">basic introduction to Groovy Closures&lt;/a> by Alex Staveley&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://www.infoq.com/presentations/grails-real-time-web">Grails and the realtime web&lt;/a>, presented by Stéphane Maldini at SpringOne2GX 2013&lt;/li>
&lt;li>Jeff Brown presented &lt;a href="http://www.infoq.com/presentations/polyglot-grails">polyglot web development with Grails 2&lt;/a>, during SpringOne 2GX 2013&lt;/li>
&lt;li>Steve Pember discusses the &lt;a href="http://www.infoq.com/presentations/grails-javascript-app">architecture, the frameworks and Responsive Design principles&lt;/a> to be employed while building a single-page JavaScript application., and why use Grails for that. Presented at GR8Conf US 2013&lt;/li>
&lt;/ul>
&lt;h2 id="screencasts">Screencasts&lt;/h2>
&lt;ul>
&lt;li>Screencast of the awesome &lt;a href="https://www.youtube.com/watch?v=3Euo6xzCwY4">support of Gradle in IntelliJ IDEA 13&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="mailing-list-discussions">Mailing-list discussions&lt;/h2>
&lt;ul>
&lt;li>Andrés Almiray follows up on the &lt;a href="http://markmail.org/message/n5x6v5rkvsqy25pp?q=griffon+list:org%2Ecodehaus%2Egriffon%2Euser+order:date-backward&amp;amp;page=1">status of Griffon 2.0&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>A possible Spock contribution could be the development of a &lt;a href="https://twitter.com/ratpackweb/status/420625975672832000">new website for Spock using the Ratpack&lt;/a> web framework&lt;/li>
&lt;li>Peter Ledbrook mentions that the &lt;a href="https://twitter.com/pledbrook/status/420501665788485632">Lazybones templates for Ratpack are now managed in the Ratpack project&lt;/a>. Also, new versions are available for the Ratpack 0.9.0 release&lt;/li>
&lt;li>Burt Beckwith &lt;a href="https://twitter.com/burtbeckwith/status/420940987754487808">warns Grails users to avoid save(failOnError: true)&lt;/a> as exceptions are costly, as demonstrated in this article on the &lt;a href="http://shipilev.net/blog/2014/exceptional-performance/">cost of using exceptions&lt;/a>&lt;/li>
&lt;li>Baruch Sadogursky is a fan of the &lt;a href="http://www.ratpack.io/">Ratpack&lt;/a> framework and found its &lt;a href="https://twitter.com/jbaruch/status/420486836818309120">website one of the most stylish&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="google-post">Google+ post&lt;/h2>
&lt;ul>
&lt;li>Mark Perry shows example of &lt;a href="https://plus.google.com/b/101432359761228268146/103753917802203497881/posts/UH2XLq7WtMb?cfem=1">&amp;ldquo;lenses&amp;rdquo; and &amp;ldquo;state monads&amp;rdquo;&lt;/a> for some more advanced functional programming with Groovy&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippet">Code snippet&lt;/h2>
&lt;ul>
&lt;li>Václav Pech &lt;a href="https://github.com/GPars/GPars/blob/master/src/test/groovy/groovyx/gpars/samples/dataflow/DemoLazyTaskDependencies.groovy">demonstrates use of GPars lazyTasks&lt;/a> to lazily and asynchronously load mutually dependent components into memory&lt;/li>
&lt;li>Tim Yates &lt;a href="https://gist.github.com/timyates/8318143">rewrites Carin Meier&amp;rsquo;s&lt;/a> (@gigasquid) &lt;a href="http://www.infoq.com/presentations/Why-is-a-Monad-Like-a-Writing-Desk">fun presentation on Monads&lt;/a> in Groovy&lt;/li>
&lt;li>Mike Mitterer shows how to &lt;a href="https://plus.google.com/u/0/+MikeMitterer/posts/Wnjzg8cGLV6">show the Git tag version in your Gradle build version&lt;/a>&lt;/li>
&lt;li>Tim Yates is also &lt;a href="https://gist.github.com/timyates/8416286">playing with functional lenses&lt;/a> in Groovy&lt;/li>
&lt;/ul>
&lt;h2 id="jobs">Jobs&lt;/h2>
&lt;ul>
&lt;li>Looking for a &lt;a href="http://findgrailsjobs.com/job/466-grails-developer">Grails job in Gibraltar&lt;/a>?&lt;/li>
&lt;li>A &lt;a href="http://findgrailsjobs.com/job/467-grails-engineering">Grails engineering job at WIBAX in Milan&lt;/a>, Italy&lt;/li>
&lt;/ul>
&lt;h2 id="other-news">Other news&lt;/h2>
&lt;ul>
&lt;li>As usual, the the &lt;a href="http://grydeske.net/news/show/25">Grails Diary&lt;/a> is out, week 2 of 2014, by Jacob Aae Mikkelsen&lt;/li>
&lt;/ul>
&lt;h2 id="books">Books&lt;/h2>
&lt;ul>
&lt;li>Dave Klein and Ben Klein&amp;rsquo;s book, &lt;a href="http://pragprog.com/book/dkgrails2/grails-2-a-quick-start-guide">&amp;ldquo;Grails 2: A Quick-Start Guide&amp;rdquo;, is now in print and shipping&lt;/a>&lt;/li>
&lt;li>Packt Publishing is &lt;a href="https://plus.google.com/u/0/111223528095963043865/posts/Vf6D9yexTuC?cfem=1">looking for reviewers for the Groovy 2 Cookbook book&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>Luke Daley is returning to his native Australia, but before leaving, he&amp;rsquo;ll be giving one &lt;a href="http://www.meetup.com/london-ggug/events/155734632/">last talk at the London Groovy Grails User Group on Gradle, Ratpack&lt;/a> and more, on January 20th&lt;/li>
&lt;li>The &lt;a href="http://cfp.gr8conf.org/login/auth">Call for Papers&lt;/a> for the GR8Conf Europe (Copenhagen, Denmark, on June 2nd-4th 2014) and GR8Conf US (Minneapolis, USA, on July 28th-29th 2014) conferences is now open&lt;/li>
&lt;li>The Call for Papers for the &lt;a href="http://greach.es/">Greach&lt;/a> conference (Madrid, Spain, on March 28th and 29th 2014) is also open, till January 31st&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>Here’s the fourth edition of the Groovy Weekly column! The holidays are gone, and tons of news are here for your consumption.</p>
<h2 id="releases">Releases</h2>
<ul>
<li>Jim Northrop <a href="https://groups.google.com/forum/#!topic/caelyf/RAEXqBpDsSA">released Caelyf 1.1.2</a>, the lightweight Groovy web toolkit for Cloud Foundry</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Guillaume Laforge wrote a small tutorial on <a href="https://github.com/glaforge/ratpack-demo-cloudfoundry/blob/master/README.md">how to deploy a Ratpack application to Cloud Foundry</a>, with a dedicated buildpack developed by Ben Hale</li>
<li>Cédric Champeau blogs about the upcoming <a href="http://melix.github.io/blog/2014/01/closure_param_inference.html">closure parameter type inference</a> for Groovy</li>
<li>Guillaume Laforge shares the <a href="http://bit.ly/gdc9notes">notes of the last Groovy developer meeting</a> in London prior to the Groovy Grails eXchance conference, covering various topics about the roadmap of Groovy 2.3 and Groovy 3.0.</li>
<li>Peter Niederwieser authored a document explaining <a href="https://github.com/spockframework/spock/blob/groovy-1.8/CONTRIBUTING.md">how to contribute to the development of the Spock</a> testing framework</li>
<li>Ken Kousen uses <a href="https://weblogs.java.net/blog/manningpubs/archive/2014/01/07/groovy-weather-pogos-gson-and-open-weather-ken-kousen-making-java-groovy-45-savings">Groovy, POGOs, GSON and the Open Weather API</a></li>
<li>Dustin Marx blogs about <a href="http://java.dzone.com/articles/identifying-gradle-conventions">identifying Gradle conventions</a></li>
<li>Noam Tenne explains how to <a href="http://blog.10ne.org/2014/01/08/registering-new-spring-beans-in-grails-during-runtime/">register new Spring beans in Grails at runtime</a></li>
<li>New Relic, the app performance management solution, adds <a href="http://blog.newrelic.com/2014/01/08/new-relic-supports-grails/">Groovy and Grails support to its Java agent</a></li>
<li>There&rsquo;s now a <a href="http://blog.testfairy.com/testfairy-gradle-plugin/">TestFairy plugin for Gradle</a>, to test Android application with the <a href="http://www.testfairy.com/">TestFairy</a> platform</li>
<li>Amit Jain from IntelliGrape, organizers of the GrailsConf India conference, is listing <a href="http://www.indicthreads.com/10774/15-reasons-grails-web-application-framework/">15 reasons why developers should use the Grails</a> web application framework</li>
<li>Amit Jain from IntelliGrape is writing about <a href="http://www.intelligrape.com/blog/2014/01/10/spock-confinemetaclasschanges-annotation-made-writing-grails-unit-test-easier/">Spock&rsquo;s @ConfineMetaClassChanges annotation</a> to make writing grails unit test easier</li>
<li>The <a href="http://www.itexto.com.br/devkico/en/?p=47">social value of Groovy and Grails in Brazil</a> by Henrique Lobo Weissmann</li>
<li>Dustin Marx explains how to <a href="http://www.javacodegeeks.com/2014/01/simple-gradle-java-plugin-customization.html">customize the Gradle Java plugin</a></li>
<li>Stergios Papadimitriou shows that <a href="https://code.google.com/p/jlabgroovy/wiki/JavaFFTvsNative">Fast Fourier Transforms can be performed in GroovyLab faster than plain C</a>, and as fast as optimized C routines</li>
<li>Peter Ledbrook <a href="http://grails.org/plugin/searchable">updated the Grails Searchable plugin installation instructions</a> to show how to make it work with Grails 2.3</li>
<li>Ondrej Kvasnovsky blogs about <a href="https://vaadin.com/blog/-/blogs/grails-integration-with-vaadin">Grails integration with Vaadin</a></li>
<li>A <a href="http://java.dzone.com/articles/closures-groovy">basic introduction to Groovy Closures</a> by Alex Staveley</li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li><a href="http://www.infoq.com/presentations/grails-real-time-web">Grails and the realtime web</a>, presented by Stéphane Maldini at SpringOne2GX 2013</li>
<li>Jeff Brown presented <a href="http://www.infoq.com/presentations/polyglot-grails">polyglot web development with Grails 2</a>, during SpringOne 2GX 2013</li>
<li>Steve Pember discusses the <a href="http://www.infoq.com/presentations/grails-javascript-app">architecture, the frameworks and Responsive Design principles</a> to be employed while building a single-page JavaScript application., and why use Grails for that. Presented at GR8Conf US 2013</li>
</ul>
<h2 id="screencasts">Screencasts</h2>
<ul>
<li>Screencast of the awesome <a href="https://www.youtube.com/watch?v=3Euo6xzCwY4">support of Gradle in IntelliJ IDEA 13</a></li>
</ul>
<h2 id="mailing-list-discussions">Mailing-list discussions</h2>
<ul>
<li>Andrés Almiray follows up on the <a href="http://markmail.org/message/n5x6v5rkvsqy25pp?q=griffon+list:org%2Ecodehaus%2Egriffon%2Euser+order:date-backward&amp;page=1">status of Griffon 2.0</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>A possible Spock contribution could be the development of a <a href="https://twitter.com/ratpackweb/status/420625975672832000">new website for Spock using the Ratpack</a> web framework</li>
<li>Peter Ledbrook mentions that the <a href="https://twitter.com/pledbrook/status/420501665788485632">Lazybones templates for Ratpack are now managed in the Ratpack project</a>. Also, new versions are available for the Ratpack 0.9.0 release</li>
<li>Burt Beckwith <a href="https://twitter.com/burtbeckwith/status/420940987754487808">warns Grails users to avoid save(failOnError: true)</a> as exceptions are costly, as demonstrated in this article on the <a href="http://shipilev.net/blog/2014/exceptional-performance/">cost of using exceptions</a></li>
<li>Baruch Sadogursky is a fan of the <a href="http://www.ratpack.io/">Ratpack</a> framework and found its <a href="https://twitter.com/jbaruch/status/420486836818309120">website one of the most stylish</a></li>
</ul>
<h2 id="google-post">Google+ post</h2>
<ul>
<li>Mark Perry shows example of <a href="https://plus.google.com/b/101432359761228268146/103753917802203497881/posts/UH2XLq7WtMb?cfem=1">&ldquo;lenses&rdquo; and &ldquo;state monads&rdquo;</a> for some more advanced functional programming with Groovy</li>
</ul>
<h2 id="code-snippet">Code snippet</h2>
<ul>
<li>Václav Pech <a href="https://github.com/GPars/GPars/blob/master/src/test/groovy/groovyx/gpars/samples/dataflow/DemoLazyTaskDependencies.groovy">demonstrates use of GPars lazyTasks</a> to lazily and asynchronously load mutually dependent components into memory</li>
<li>Tim Yates <a href="https://gist.github.com/timyates/8318143">rewrites Carin Meier&rsquo;s</a> (@gigasquid) <a href="http://www.infoq.com/presentations/Why-is-a-Monad-Like-a-Writing-Desk">fun presentation on Monads</a> in Groovy</li>
<li>Mike Mitterer shows how to <a href="https://plus.google.com/u/0/+MikeMitterer/posts/Wnjzg8cGLV6">show the Git tag version in your Gradle build version</a></li>
<li>Tim Yates is also <a href="https://gist.github.com/timyates/8416286">playing with functional lenses</a> in Groovy</li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li>Looking for a <a href="http://findgrailsjobs.com/job/466-grails-developer">Grails job in Gibraltar</a>?</li>
<li>A <a href="http://findgrailsjobs.com/job/467-grails-engineering">Grails engineering job at WIBAX in Milan</a>, Italy</li>
</ul>
<h2 id="other-news">Other news</h2>
<ul>
<li>As usual, the the <a href="http://grydeske.net/news/show/25">Grails Diary</a> is out, week 2 of 2014, by Jacob Aae Mikkelsen</li>
</ul>
<h2 id="books">Books</h2>
<ul>
<li>Dave Klein and Ben Klein&rsquo;s book, <a href="http://pragprog.com/book/dkgrails2/grails-2-a-quick-start-guide">&ldquo;Grails 2: A Quick-Start Guide&rdquo;, is now in print and shipping</a></li>
<li>Packt Publishing is <a href="https://plus.google.com/u/0/111223528095963043865/posts/Vf6D9yexTuC?cfem=1">looking for reviewers for the Groovy 2 Cookbook book</a></li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li>Luke Daley is returning to his native Australia, but before leaving, he&rsquo;ll be giving one <a href="http://www.meetup.com/london-ggug/events/155734632/">last talk at the London Groovy Grails User Group on Gradle, Ratpack</a> and more, on January 20th</li>
<li>The <a href="http://cfp.gr8conf.org/login/auth">Call for Papers</a> for the GR8Conf Europe (Copenhagen, Denmark, on June 2nd-4th 2014) and GR8Conf US (Minneapolis, USA, on July 28th-29th 2014) conferences is now open</li>
<li>The Call for Papers for the <a href="http://greach.es/">Greach</a> conference (Madrid, Spain, on March 28th and 29th 2014) is also open, till January 31st</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #3</title><link>https://glaforge.dev/posts/2014/01/07/groovy-weekly-3/</link><pubDate>Tue, 07 Jan 2014 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2014/01/07/groovy-weekly-3/</guid><description>&lt;p>Happy New Year best wishes are still flowing around, and new year resolutions have been sealed, but despite the busy times and good moments spent with our families, the Groovy ecosystem is still abuzz with news! You’d think it’d be a quiet third edition of the Groovy Weekly column, but no, we’ve all been busy with Groovy stuff! So what’s in store?&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://www.ratpack.io/versions/0.9.0/">Ratpack 0.9.0&lt;/a> has been released&lt;/li>
&lt;li>Peter Ledbrook published &lt;a href="https://bintray.com/pledbrook/lazybones-templates/lazybones/0.6/release">version 0.6 of Lazybones&lt;/a> with support for multiple template engines&lt;/li>
&lt;li>Gernot Starke released &lt;a href="https://bitbucket.org/arc42/pdfstamper/overview">v1.0 of pdfStamper&lt;/a>, a small Griffon / Groovy-based utility to add page / chapter / header to pdf files&lt;/li>
&lt;li>Ken Krebs released his version of &lt;a href="https://github.com/kktec/gsheets">gsheets&lt;/a>, a lightweight DSL wrapper over Apache POI, that is intended to make extracting data from, or pushing data to spreadsheets simple and declarative. It can be used with Groovy 1.8, Grails 2 and Java 6+.&lt;/li>
&lt;li>Yoshiya Hinosawa created a &lt;a href="https://github.com/kt3k/coveralls-gradle-plugin">Gradle plugin&lt;/a> for &lt;a href="https://coveralls.io/">CoverAlls.IO&lt;/a>, a service which gathers your code coverage for history and analytics purpose&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>Alex Staveley covers &lt;a href="http://java.dzone.com/articles/groovys-smooth-operators">Groovy&amp;rsquo;s &amp;ldquo;smooth operators&amp;rdquo;&lt;/a>: Elvis, spaceship, safe navigation, spread, field access, method references&lt;/li>
&lt;li>Mark Perry blogs about &lt;a href="http://mperry.github.io/2014/01/03/referentially-transparent-io.html">referentially transparent input / output in Groovy&lt;/a>&lt;/li>
&lt;li>Ben Limmer explains how to &lt;a href="http://benlimmer.com/2014/01/04/automatically-publish-to-sonatype-with-gradle-and-travis-ci/">automatically publish to Sonatype with Gradle and Travis CI&lt;/a>&lt;/li>
&lt;li>Michael Scharhag covers &lt;a href="http://www.mscharhag.com/2014/01/grails-hibernate-filters.html">how to use Hibernate filters in Grails&lt;/a>&lt;/li>
&lt;li>Cyril Mottier shows the &lt;a href="https://plus.google.com/118417777153109946393/posts/WRgB2shaeuu">Android Gradle build in action&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="presentations">Presentations&lt;/h2>
&lt;ul>
&lt;li>In this SpringOne2GX presentation, Hans Dockter &lt;a href="http://www.infoq.com/presentations/compare-build-tools">discusses build systems&lt;/a> from a declarative vs. imperative perspective, the importance of a rich execution model and problems with POM, providing examples from Gradle, Maven and Ant&lt;/li>
&lt;li>Ken Sipe spoke about Spock during the JavaZone conference and covers &lt;a href="http://vimeo.com/74553183">mocking, stubbing and spying with Spock&lt;/a>&lt;/li>
&lt;li>At GR8Conf US 2013, Marco Vermeulen explains using &lt;a href="http://www.infoq.com/presentations/bdd-cucumber-groovy">Cucumber and Groovy to apply BDD to a project&lt;/a>, using Gherkin for writing scenarios, Groovy for step definitions, Spock for new features, and running Cucumber features&lt;/li>
&lt;li>At SpringOne2GX 2013, in his &lt;a href="http://www.infoq.com/presentations/groovy-app-architecture">Application Architecture in Groovy&lt;/a> talk, Dan Woods presented the strengths and flexibility in Groovy, including its dynamic nature, categories and mixins, to create a powerful, maintainable, and simple application architecture&lt;/li>
&lt;li>At GR8Conf US 2013, Zan Thrash advises on &lt;a href="http://www.infoq.com/presentations/grails-nodejs">using some of the Node.js tools in order to improve Grails development&lt;/a>: lint, concat, minify, testing JavaScript files, etc.&lt;/li>
&lt;/ul>
&lt;h2 id="mailing-list-discussions">Mailing-list discussions&lt;/h2>
&lt;ul>
&lt;li>Andrés Almiray gives a &lt;a href="http://markmail.org/message/h6g4sarojl7b7mi6?q=griffon+user+list:org.codehaus.griffon.user+order:date-backward&amp;amp;page=1">status on the future Griffon 2.0&lt;/a> and asks for feedback&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/gvmtool/status/419840537924210688">Lazybones 0.6 is available through GVM&lt;/a>&lt;/li>
&lt;li>Peter Ledbrook is adding &lt;a href="https://twitter.com/pledbrook/status/417986426882777088">Handlebars templating support to Lazybones&lt;/a>&lt;/li>
&lt;li>Peter Ledbrook has &lt;a href="https://twitter.com/pledbrook/status/419021400327077888">pushed the Groovy Handlebars template engine into Bintray JCenter&lt;/a>&lt;/li>
&lt;li>Anton Arhipov tweeted about the &lt;a href="https://twitter.com/antonarhipov/status/419614242333917184">latest update of the GMaven Groovy Maven plugin&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/ratpackweb/status/419045750396694528">Work has started&lt;/a> on getting Ratpack in the &lt;a href="http://www.techempower.com/benchmarks/">TechEmpower framework benchmark&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/ratpackweb/status/417318063957377024">Testing Ratpack modules&lt;/a> and subsystem in isolations&lt;/li>
&lt;li>&lt;a href="https://twitter.com/grainframework/status/418021109066248192">reStructuredText support via JRst and PegDown markdown parser&lt;/a> are integrated into 0.6.0-SNAPSHOT of the &lt;a href="http://sysgears.com/grain/">Grain&lt;/a> static website generator&lt;/li>
&lt;/ul>
&lt;h2 id="code-snippet">Code snippet&lt;/h2>
&lt;ul>
&lt;li>Kenneth Endfinger wished us all a &lt;a href="https://plus.google.com/u/0/+KennethEndfinger/posts/g4nPwfoBGeu">Happy New Year with some Groovy asciiart&lt;/a> (&lt;a href="https://gist.github.com/kaendfinger/8220577">snippet&lt;/a>)!&lt;/li>
&lt;/ul>
&lt;h2 id="jobs">Jobs&lt;/h2>
&lt;ul>
&lt;li>Antony Jones is looking for &lt;a href="https://twitter.com/antony/status/419055221365415936">Grails contractors in London&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="other-news">Other news&lt;/h2>
&lt;ul>
&lt;li>First &lt;a href="http://grydeske.net/news/show/24">Grails diary&lt;/a> of the year by Jacob Aae Mikkelsen&lt;/li>
&lt;li>The &lt;a href="http://www.ratpack.io/manual/current/api/">Ratpack reference documentation&lt;/a> has just had a facelift&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://grailsconf.in/">GrailsConf India&lt;/a>, organized by &lt;a href="http://www.intelligrape.com/">IntelliGrape Software&lt;/a>, is taking place in New Delhi, India, on January 11th&lt;/li>
&lt;li>The &lt;a href="http://cfp.gr8conf.org/login/auth">Call for Papers&lt;/a> for the GR8Conf Europe (Copenhagen, Denmark, on June 2nd-4th 2014) and GR8Conf US (Minneapolis, USA, on July 28th-29th 2014) conferences is now open&lt;/li>
&lt;li>The Call for Papers for the &lt;a href="http://greach.es/">Greach&lt;/a> conference (Madrid, Spain, on March 28th and 29th 2014) is also open, till January 31st&lt;/li>
&lt;li>Russel Winder is going to present a &lt;a href="http://nordevcon.com/#russel_winder">workshop on Spock&lt;/a> at the Nordev conference&lt;/li>
&lt;li>Guillaume Laforge is speaking at JFokus 2014 about how to &lt;a href="http://www.jfokus.se/jfokus/talks.jsp#BenefitfromGroovynow">benefit form using Groovy, when, why and how&lt;/a>&lt;/li>
&lt;li>Cédric Champeau is speaking (in French, at the Riviera Groovy/Grails user group) about &lt;a href="http://www.meetup.com/Riviera-Groovy-Grails-User-Group/events/154634302/">how to contribute to Groovy&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Don&amp;rsquo;t forget you can &lt;a href="http://bit.ly/groovyweekly">contribute&lt;/a> to this weekly column of Groovy news, and that you can &lt;a href="http://bit.ly/groovy-weekly-subscribe">subscribe through a newsletter&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>Happy New Year best wishes are still flowing around, and new year resolutions have been sealed, but despite the busy times and good moments spent with our families, the Groovy ecosystem is still abuzz with news! You’d think it’d be a quiet third edition of the Groovy Weekly column, but no, we’ve all been busy with Groovy stuff! So what’s in store?</p>
<h2 id="releases">Releases</h2>
<ul>
<li><a href="http://www.ratpack.io/versions/0.9.0/">Ratpack 0.9.0</a> has been released</li>
<li>Peter Ledbrook published <a href="https://bintray.com/pledbrook/lazybones-templates/lazybones/0.6/release">version 0.6 of Lazybones</a> with support for multiple template engines</li>
<li>Gernot Starke released <a href="https://bitbucket.org/arc42/pdfstamper/overview">v1.0 of pdfStamper</a>, a small Griffon / Groovy-based utility to add page / chapter / header to pdf files</li>
<li>Ken Krebs released his version of <a href="https://github.com/kktec/gsheets">gsheets</a>, a lightweight DSL wrapper over Apache POI, that is intended to make extracting data from, or pushing data to spreadsheets simple and declarative. It can be used with Groovy 1.8, Grails 2 and Java 6+.</li>
<li>Yoshiya Hinosawa created a <a href="https://github.com/kt3k/coveralls-gradle-plugin">Gradle plugin</a> for <a href="https://coveralls.io/">CoverAlls.IO</a>, a service which gathers your code coverage for history and analytics purpose</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>Alex Staveley covers <a href="http://java.dzone.com/articles/groovys-smooth-operators">Groovy&rsquo;s &ldquo;smooth operators&rdquo;</a>: Elvis, spaceship, safe navigation, spread, field access, method references</li>
<li>Mark Perry blogs about <a href="http://mperry.github.io/2014/01/03/referentially-transparent-io.html">referentially transparent input / output in Groovy</a></li>
<li>Ben Limmer explains how to <a href="http://benlimmer.com/2014/01/04/automatically-publish-to-sonatype-with-gradle-and-travis-ci/">automatically publish to Sonatype with Gradle and Travis CI</a></li>
<li>Michael Scharhag covers <a href="http://www.mscharhag.com/2014/01/grails-hibernate-filters.html">how to use Hibernate filters in Grails</a></li>
<li>Cyril Mottier shows the <a href="https://plus.google.com/118417777153109946393/posts/WRgB2shaeuu">Android Gradle build in action</a></li>
</ul>
<h2 id="presentations">Presentations</h2>
<ul>
<li>In this SpringOne2GX presentation, Hans Dockter <a href="http://www.infoq.com/presentations/compare-build-tools">discusses build systems</a> from a declarative vs. imperative perspective, the importance of a rich execution model and problems with POM, providing examples from Gradle, Maven and Ant</li>
<li>Ken Sipe spoke about Spock during the JavaZone conference and covers <a href="http://vimeo.com/74553183">mocking, stubbing and spying with Spock</a></li>
<li>At GR8Conf US 2013, Marco Vermeulen explains using <a href="http://www.infoq.com/presentations/bdd-cucumber-groovy">Cucumber and Groovy to apply BDD to a project</a>, using Gherkin for writing scenarios, Groovy for step definitions, Spock for new features, and running Cucumber features</li>
<li>At SpringOne2GX 2013, in his <a href="http://www.infoq.com/presentations/groovy-app-architecture">Application Architecture in Groovy</a> talk, Dan Woods presented the strengths and flexibility in Groovy, including its dynamic nature, categories and mixins, to create a powerful, maintainable, and simple application architecture</li>
<li>At GR8Conf US 2013, Zan Thrash advises on <a href="http://www.infoq.com/presentations/grails-nodejs">using some of the Node.js tools in order to improve Grails development</a>: lint, concat, minify, testing JavaScript files, etc.</li>
</ul>
<h2 id="mailing-list-discussions">Mailing-list discussions</h2>
<ul>
<li>Andrés Almiray gives a <a href="http://markmail.org/message/h6g4sarojl7b7mi6?q=griffon+user+list:org.codehaus.griffon.user+order:date-backward&amp;page=1">status on the future Griffon 2.0</a> and asks for feedback</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li><a href="https://twitter.com/gvmtool/status/419840537924210688">Lazybones 0.6 is available through GVM</a></li>
<li>Peter Ledbrook is adding <a href="https://twitter.com/pledbrook/status/417986426882777088">Handlebars templating support to Lazybones</a></li>
<li>Peter Ledbrook has <a href="https://twitter.com/pledbrook/status/419021400327077888">pushed the Groovy Handlebars template engine into Bintray JCenter</a></li>
<li>Anton Arhipov tweeted about the <a href="https://twitter.com/antonarhipov/status/419614242333917184">latest update of the GMaven Groovy Maven plugin</a></li>
<li><a href="https://twitter.com/ratpackweb/status/419045750396694528">Work has started</a> on getting Ratpack in the <a href="http://www.techempower.com/benchmarks/">TechEmpower framework benchmark</a></li>
<li><a href="https://twitter.com/ratpackweb/status/417318063957377024">Testing Ratpack modules</a> and subsystem in isolations</li>
<li><a href="https://twitter.com/grainframework/status/418021109066248192">reStructuredText support via JRst and PegDown markdown parser</a> are integrated into 0.6.0-SNAPSHOT of the <a href="http://sysgears.com/grain/">Grain</a> static website generator</li>
</ul>
<h2 id="code-snippet">Code snippet</h2>
<ul>
<li>Kenneth Endfinger wished us all a <a href="https://plus.google.com/u/0/+KennethEndfinger/posts/g4nPwfoBGeu">Happy New Year with some Groovy asciiart</a> (<a href="https://gist.github.com/kaendfinger/8220577">snippet</a>)!</li>
</ul>
<h2 id="jobs">Jobs</h2>
<ul>
<li>Antony Jones is looking for <a href="https://twitter.com/antony/status/419055221365415936">Grails contractors in London</a></li>
</ul>
<h2 id="other-news">Other news</h2>
<ul>
<li>First <a href="http://grydeske.net/news/show/24">Grails diary</a> of the year by Jacob Aae Mikkelsen</li>
<li>The <a href="http://www.ratpack.io/manual/current/api/">Ratpack reference documentation</a> has just had a facelift</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="http://grailsconf.in/">GrailsConf India</a>, organized by <a href="http://www.intelligrape.com/">IntelliGrape Software</a>, is taking place in New Delhi, India, on January 11th</li>
<li>The <a href="http://cfp.gr8conf.org/login/auth">Call for Papers</a> for the GR8Conf Europe (Copenhagen, Denmark, on June 2nd-4th 2014) and GR8Conf US (Minneapolis, USA, on July 28th-29th 2014) conferences is now open</li>
<li>The Call for Papers for the <a href="http://greach.es/">Greach</a> conference (Madrid, Spain, on March 28th and 29th 2014) is also open, till January 31st</li>
<li>Russel Winder is going to present a <a href="http://nordevcon.com/#russel_winder">workshop on Spock</a> at the Nordev conference</li>
<li>Guillaume Laforge is speaking at JFokus 2014 about how to <a href="http://www.jfokus.se/jfokus/talks.jsp#BenefitfromGroovynow">benefit form using Groovy, when, why and how</a></li>
<li>Cédric Champeau is speaking (in French, at the Riviera Groovy/Grails user group) about <a href="http://www.meetup.com/Riviera-Groovy-Grails-User-Group/events/154634302/">how to contribute to Groovy</a></li>
</ul>
<p>Don&rsquo;t forget you can <a href="http://bit.ly/groovyweekly">contribute</a> to this weekly column of Groovy news, and that you can <a href="http://bit.ly/groovy-weekly-subscribe">subscribe through a newsletter</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #2</title><link>https://glaforge.dev/posts/2013/12/31/groovy-weekly-2/</link><pubDate>Tue, 31 Dec 2013 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2013/12/31/groovy-weekly-2/</guid><description>&lt;p>For the second Groovy Weekly column, on the eve of a new year, I’d like to wish you a very Groovy year, and share with you the following links, hoping you’ll have some spare time to look into them while you’re celebrating.&lt;/p>
&lt;h2 id="releases">Releases&lt;/h2>
&lt;ul>
&lt;li>A new project appeared on the Groovy ecosystem radar: Grain, &lt;a href="http://groovy.329449.n5.nabble.com/ANN-Grain-0-5-0-released-td5717888.html">announced&lt;/a> by Victor Vlasenko on the Groovy mailing-list. &lt;a href="http://sysgears.com/grain/">Grain&lt;/a> is a promising lightweight and yet powerful static website generator for Groovy which purpose is to make demanding site implementation an intuitive and enjoyable. Grain framework is equally well-suited for any static website, whether it is a complex and sophisticated company site or a simple and neat blog. The framework applies elegant website building concepts and allows to develop rapidly by making and seeing changes on the fly.&lt;/li>
&lt;li>Kunal Dabir created a &lt;a href="https://github.com/kdabir/lazybone-templates/blob/master/templates/groovy-lib/README.md">Lazybone template for creating Groovy libraries&lt;/a> readily publishable to Bintray, CI with Travis-CI, with a Gradle build and wrapper, a .gitignore file and more.&lt;/li>
&lt;li>Last week we mentioned the release of CodeNarc 0.20, and the associated &lt;a href="http://groovy.329449.n5.nabble.com/ANN-Announcing-Eclipse-CodeNarc-Plugin-0-20-0-td5717876.html">CodeNarc Eclipse plugin&lt;/a> has also been released&lt;/li>
&lt;li>Andrés Almiray released &lt;a href="https://github.com/aalmiray/gipsy">Gipsy&lt;/a>, an AST transformation to simplify the use of the Service Provider Interface, similar to the &lt;a href="https://github.com/aalmiray/jipsy">Jipsy&lt;/a> annotation processing toolkit he also created:&lt;/li>
&lt;li>Stergios Papadimitriou shares with us his project, &lt;a href="https://code.google.com/p/jlabgroovy/">GroovyLab&lt;/a>, a MATLAB-like environment for the Java Virtual Machine. GroovyLab is an open source project based on the Groovy language. GroovyLab is efficient and can be an interesting open-source alternative to commercial packages, especially for the scientific community familiar with Java. The article introduces the architecture of GroovyLab and presents some examples of using GroovyLab to do useful work.&lt;/li>
&lt;/ul>
&lt;h2 id="articles">Articles&lt;/h2>
&lt;ul>
&lt;li>A very extensive &lt;a href="http://danveloper.github.io/ggx-wrapdown.html">Groovy / Grails eXchange wrapdown post&lt;/a> by Dan Woods covering all the talks he attended in detail&lt;/li>
&lt;li>Dustin Marx covers some &lt;a href="http://java.dzone.com/articles/gradle-command-line">Gradle command-line conveniences&lt;/a>&lt;/li>
&lt;li>Dustin Marx &lt;a href="http://java.dzone.com/articles/book-review-gradle-beyond">reviews the book by Tim Berglund: &amp;ldquo;Gradle, beyond the basics&amp;rdquo;&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="presentations-from-springone2gx">Presentations from SpringOne2GX&lt;/h2>
&lt;ul>
&lt;li>In this presentation on InfoQ, recorded at SpringOne2GX, Burt Beckwith discusses performing &lt;a href="http://www.infoq.com/presentations/grails-transaction-2gx">transactions in Grails&lt;/a>, covering services, customizing transaction attributes (isolation, propagation levels), two-phase commit, using JMS, and testing the code&lt;/li>
&lt;li>Again recorded at SpringOne2GX, Paul King speaks about &lt;a href="http://www.infoq.com/presentations/groovy-dsl-business-rule">leveraging Groovy for capturing business rules&lt;/a>, illustrated with various DSLs written in Groovy, highlighting several logic solving APIs and looks at the pros and cons of the various approaches (including tool support, flexibility, lock-in)&lt;/li>
&lt;/ul>
&lt;h2 id="interviews">Interviews&lt;/h2>
&lt;ul>
&lt;li>Stephen Chin &lt;a href="http://nighthacking.com/interview-with-luke-daley-on-geb-and-ratpack/">interviews Luke Daley&lt;/a> during the Devoxx conference, about Ratpack, Gradle, Geb&lt;/li>
&lt;/ul>
&lt;h2 id="mailing-list-discussions">Mailing-list discussions&lt;/h2>
&lt;ul>
&lt;li>Thibault Kruse discusses &lt;a href="http://groovy.329449.n5.nabble.com/groovysh-improvements-td5717851.html">possible improvements to the groovysh&lt;/a> command-line shell in this mailing-list discussion. Don&amp;rsquo;t hesitate to provide your input!&lt;/li>
&lt;/ul>
&lt;h2 id="tweets">Tweets&lt;/h2>
&lt;ul>
&lt;li>Luke Daley mentions his testing of the &lt;a href="https://waffle.io/">Waffle.IO service&lt;/a> (a nice kanban board representation of Github issues) to organize and keep track of the &lt;a href="https://waffle.io/ratpack/ratpack">backlog, ongoing development of Ratpack&lt;/a>&lt;/li>
&lt;li>Dan Woods &lt;a href="https://twitter.com/ratpackweb/status/416501698119892992">joins the Ratpack project as a new committer&lt;/a>&lt;/li>
&lt;li>Ratpack now supports &lt;a href="https://twitter.com/ratpackweb/status/416854893677514752">“fat jar” deployment&lt;/a>&lt;/li>
&lt;li>Bert van den Brande used the &lt;a href="https://github.com/johnrengelman/shadow">Gradle Shadow plugin&lt;/a> to create an executable JAR in no time&lt;/li>
&lt;li>Václav Pech, GPars project lead, is &lt;a href="https://twitter.com/vaclav_pech/status/417944625643782144">sprinkling a few Groovy&amp;rsquo;s few @DelegatesTo annotations over the GPars codebase operators&lt;/a> to make them more @CompileStatic-friendly&lt;/li>
&lt;/ul>
&lt;h2 id="job">Job&lt;/h2>
&lt;ul>
&lt;li>Dan Woods relays the job post from @good_technology that is &lt;a href="http://www.startuply.com/Jobs/Software_Architect_SaaS_Platform_6473_3.aspx">looking for a Grails developer in San Francisco&lt;/a>, with mobile skills as well&lt;/li>
&lt;/ul>
&lt;h2 id="other-news">Other news&lt;/h2>
&lt;ul>
&lt;li>The last &lt;a href="http://grydeske.net/news/show/23">Grails diary&lt;/a> of the year from by Jacob Aae Mikkelsen, if you want to be up-to-date on all Grails plugins news&lt;/li>
&lt;li>Bobby Warner contributes &lt;a href="https://github.com/ratpack/ratpack/pull/222">support for the PATCH method to the Ratpack&lt;/a> project&lt;/li>
&lt;/ul>
&lt;h2 id="events">Events&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://grailsconf.in/">GrailsConf India&lt;/a>, organized by &lt;a href="http://www.intelligrape.com/">IntelliGrape Software&lt;/a>, is taking place in New Delhi, India, on January 11th&lt;/li>
&lt;li>The &lt;a href="http://cfp.gr8conf.org/login/auth">Call for Papers&lt;/a> for the GR8Conf Europe (Copenhagen, Denmark, on June 2nd-4th 2014) and GR8Conf US (Minneapolis, USA, on July 28th-29th 2014) conferences is now open&lt;/li>
&lt;li>The Call for Papers for the &lt;a href="http://greach.es/">Greach&lt;/a> conference (Madrid, Spain, on March 28th and 29th 2014) is also open, till January 31st&lt;/li>
&lt;/ul>
&lt;h2 id="happy-new-year">Happy New Year!&lt;/h2>
&lt;p>Almost the end of the year! On behalf of the Groovy development team, let me wish you the best and grooviest year for 2014!&lt;/p></description><content:encoded>
<![CDATA[<p>For the second Groovy Weekly column, on the eve of a new year, I’d like to wish you a very Groovy year, and share with you the following links, hoping you’ll have some spare time to look into them while you’re celebrating.</p>
<h2 id="releases">Releases</h2>
<ul>
<li>A new project appeared on the Groovy ecosystem radar: Grain, <a href="http://groovy.329449.n5.nabble.com/ANN-Grain-0-5-0-released-td5717888.html">announced</a> by Victor Vlasenko on the Groovy mailing-list. <a href="http://sysgears.com/grain/">Grain</a> is a promising lightweight and yet powerful static website generator for Groovy which purpose is to make demanding site implementation an intuitive and enjoyable. Grain framework is equally well-suited for any static website, whether it is a complex and sophisticated company site or a simple and neat blog. The framework applies elegant website building concepts and allows to develop rapidly by making and seeing changes on the fly.</li>
<li>Kunal Dabir created a <a href="https://github.com/kdabir/lazybone-templates/blob/master/templates/groovy-lib/README.md">Lazybone template for creating Groovy libraries</a> readily publishable to Bintray, CI with Travis-CI, with a Gradle build and wrapper, a .gitignore file and more.</li>
<li>Last week we mentioned the release of CodeNarc 0.20, and the associated <a href="http://groovy.329449.n5.nabble.com/ANN-Announcing-Eclipse-CodeNarc-Plugin-0-20-0-td5717876.html">CodeNarc Eclipse plugin</a> has also been released</li>
<li>Andrés Almiray released <a href="https://github.com/aalmiray/gipsy">Gipsy</a>, an AST transformation to simplify the use of the Service Provider Interface, similar to the <a href="https://github.com/aalmiray/jipsy">Jipsy</a> annotation processing toolkit he also created:</li>
<li>Stergios Papadimitriou shares with us his project, <a href="https://code.google.com/p/jlabgroovy/">GroovyLab</a>, a MATLAB-like environment for the Java Virtual Machine. GroovyLab is an open source project based on the Groovy language. GroovyLab is efficient and can be an interesting open-source alternative to commercial packages, especially for the scientific community familiar with Java. The article introduces the architecture of GroovyLab and presents some examples of using GroovyLab to do useful work.</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>A very extensive <a href="http://danveloper.github.io/ggx-wrapdown.html">Groovy / Grails eXchange wrapdown post</a> by Dan Woods covering all the talks he attended in detail</li>
<li>Dustin Marx covers some <a href="http://java.dzone.com/articles/gradle-command-line">Gradle command-line conveniences</a></li>
<li>Dustin Marx <a href="http://java.dzone.com/articles/book-review-gradle-beyond">reviews the book by Tim Berglund: &ldquo;Gradle, beyond the basics&rdquo;</a></li>
</ul>
<h2 id="presentations-from-springone2gx">Presentations from SpringOne2GX</h2>
<ul>
<li>In this presentation on InfoQ, recorded at SpringOne2GX, Burt Beckwith discusses performing <a href="http://www.infoq.com/presentations/grails-transaction-2gx">transactions in Grails</a>, covering services, customizing transaction attributes (isolation, propagation levels), two-phase commit, using JMS, and testing the code</li>
<li>Again recorded at SpringOne2GX, Paul King speaks about <a href="http://www.infoq.com/presentations/groovy-dsl-business-rule">leveraging Groovy for capturing business rules</a>, illustrated with various DSLs written in Groovy, highlighting several logic solving APIs and looks at the pros and cons of the various approaches (including tool support, flexibility, lock-in)</li>
</ul>
<h2 id="interviews">Interviews</h2>
<ul>
<li>Stephen Chin <a href="http://nighthacking.com/interview-with-luke-daley-on-geb-and-ratpack/">interviews Luke Daley</a> during the Devoxx conference, about Ratpack, Gradle, Geb</li>
</ul>
<h2 id="mailing-list-discussions">Mailing-list discussions</h2>
<ul>
<li>Thibault Kruse discusses <a href="http://groovy.329449.n5.nabble.com/groovysh-improvements-td5717851.html">possible improvements to the groovysh</a> command-line shell in this mailing-list discussion. Don&rsquo;t hesitate to provide your input!</li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Luke Daley mentions his testing of the <a href="https://waffle.io/">Waffle.IO service</a> (a nice kanban board representation of Github issues) to organize and keep track of the <a href="https://waffle.io/ratpack/ratpack">backlog, ongoing development of Ratpack</a></li>
<li>Dan Woods <a href="https://twitter.com/ratpackweb/status/416501698119892992">joins the Ratpack project as a new committer</a></li>
<li>Ratpack now supports <a href="https://twitter.com/ratpackweb/status/416854893677514752">“fat jar” deployment</a></li>
<li>Bert van den Brande used the <a href="https://github.com/johnrengelman/shadow">Gradle Shadow plugin</a> to create an executable JAR in no time</li>
<li>Václav Pech, GPars project lead, is <a href="https://twitter.com/vaclav_pech/status/417944625643782144">sprinkling a few Groovy&rsquo;s few @DelegatesTo annotations over the GPars codebase operators</a> to make them more @CompileStatic-friendly</li>
</ul>
<h2 id="job">Job</h2>
<ul>
<li>Dan Woods relays the job post from @good_technology that is <a href="http://www.startuply.com/Jobs/Software_Architect_SaaS_Platform_6473_3.aspx">looking for a Grails developer in San Francisco</a>, with mobile skills as well</li>
</ul>
<h2 id="other-news">Other news</h2>
<ul>
<li>The last <a href="http://grydeske.net/news/show/23">Grails diary</a> of the year from by Jacob Aae Mikkelsen, if you want to be up-to-date on all Grails plugins news</li>
<li>Bobby Warner contributes <a href="https://github.com/ratpack/ratpack/pull/222">support for the PATCH method to the Ratpack</a> project</li>
</ul>
<h2 id="events">Events</h2>
<ul>
<li><a href="http://grailsconf.in/">GrailsConf India</a>, organized by <a href="http://www.intelligrape.com/">IntelliGrape Software</a>, is taking place in New Delhi, India, on January 11th</li>
<li>The <a href="http://cfp.gr8conf.org/login/auth">Call for Papers</a> for the GR8Conf Europe (Copenhagen, Denmark, on June 2nd-4th 2014) and GR8Conf US (Minneapolis, USA, on July 28th-29th 2014) conferences is now open</li>
<li>The Call for Papers for the <a href="http://greach.es/">Greach</a> conference (Madrid, Spain, on March 28th and 29th 2014) is also open, till January 31st</li>
</ul>
<h2 id="happy-new-year">Happy New Year!</h2>
<p>Almost the end of the year! On behalf of the Groovy development team, let me wish you the best and grooviest year for 2014!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Weekly #1</title><link>https://glaforge.dev/posts/2013/12/24/groovy-weekly-1/</link><pubDate>Tue, 24 Dec 2013 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2013/12/24/groovy-weekly-1/</guid><description>&lt;p>Welcome to the Groovy Weekly news brief!&lt;/p>
&lt;p>As the name implies, I’m going to try to make regular (in theory on a weekly basis) column of all the interesting news, presentations, code snippets, events, conferences related to the Groovy ecosystem.&lt;/p>
&lt;p>And as a Christmas present, here’s the first issue!&lt;/p>
&lt;p>Your feedback is important, and we’d be happy to hear about your thoughts on a regular column about Groovy related news: what are your expectations, what you’d like to hear about, what news bits are of higher interest to you, how you would like this information to be delivered, etc.&lt;/p></description><content:encoded>
<![CDATA[<p>Welcome to the Groovy Weekly news brief!</p>
<p>As the name implies, I’m going to try to make regular (in theory on a weekly basis) column of all the interesting news, presentations, code snippets, events, conferences related to the Groovy ecosystem.</p>
<p>And as a Christmas present, here’s the first issue!</p>
<p>Your feedback is important, and we’d be happy to hear about your thoughts on a regular column about Groovy related news: what are your expectations, what you’d like to hear about, what news bits are of higher interest to you, how you would like this information to be delivered, etc.</p>
<p>As a first step, we’ll cover links to articles, blog posts, tweets, presentations, releases, events, books, code snippets. But you can contribute too! I’ve setup a Google Form where you can fill in a few fields with a link, a description, the kind of information you’re sharing, etc. So feel free to submit interesting information through this form here:</p>
<p><a href="http://bit.ly/groovyweekly">http://bit.ly/groovyweekly</a></p>
<p>The first Groovy Weekly edition will be posted on my personal blog, and the link will be spread on various channels (mailing-list, twitter, Google+, etc) but I’m wondering how to best deliver this information. I’m thinking particularly of setting up a newsletter format, for instance with a service like Mailchimp, so that people could opt in and subscribe to receive this weekly column directly in their inbox.</p>
<p>In the meantime, and on behalf of the Groovy development team, we wish you the best for the holidays! A Groovy Christmas and a Groovy New Year!</p>
<h2 id="releases">Releases</h2>
<ul>
<li>a bug fix release with <a href="http://grails.org/2.3.4%20Release%20Notes">Grails 2.3.4</a></li>
<li><a href="http://forums.gradle.org/gradle/topics/gradle_1_10_released">Gradle 1.10</a> was released with improved progress reporting, executing specific tests from the command-line, shouldRunAfter task ordering, further C and C++ support</li>
<li>a new version of the <a href="http://groovy.329449.n5.nabble.com/ANN-Announcing-CodeNarc-0-20-td5717822.html">CodeNarc 0.20</a> static analysis tool for Groovy with new rules, as well as updated and enhanced rules</li>
</ul>
<h2 id="articles">Articles</h2>
<ul>
<li>The <a href="http://groovy.codehaus.org/Roadmap">Groovy roadmap</a> page has recently been udpated, giving a better view of the coming releases of Groovy 2.3 and 3.0, and their respective content</li>
<li>Burt Beckwith explains how to automatically <a href="http://burtbeckwith.com/blog/?p=2017">convert password hashes in Grails spring-security-core</a></li>
<li>Roy Clarkson shows how you can easily <a href="https://spring.io/blog/2013/12/19/serving-static-web-content-with-spring-boot">serve static web content with Spring Boot</a>, thanks to a little Groovy Spring controller</li>
<li>Dusting Marx covers how to <a href="http://java.dzone.com/articles/searching-subversion-logs">search Subversion logs with Groovy</a></li>
<li>MrHaki demonstrates <a href="http://mrhaki.blogspot.dk/2013/12/groovy-goodness-using-package-scoped.html">how to use the @PackageScope transformation with methods, fields and classes</a></li>
<li>Dustin Marx writes about the <a href="http://www.javacodegeeks.com/2013/12/sublime-simplicity-of-scripting-with-groovy.html">sublime simplicity of scripting with Groovy</a></li>
<li>Simon Temple talks about <a href="http://simontemple.blogspot.co.uk/2013/12/groovy-in-cloud-sandboxing-and-more.html">sandboxing Groovy code</a> through an AST transformation that examines all method invocations to check if they are allowed or not</li>
<li>Lieven Doclo demonstrates <a href="http://groovy.dzone.com/articles/rest-prototyping-spark-and">rapid REST prototyping with Groovy</a> and the <a href="http://www.sparkjava.com/">Spark</a> micro web framework for the Java platform</li>
<li>Interview about how &ldquo;<a href="http://searchvmware.techtarget.com/feature/VMware-surmounts-learning-curve-by-developing-Project-NEE-platform">VMware surmounts learning curve by developing Project NEE platform</a>&rdquo; with Groovy and Grails</li>
</ul>
<p>A particular highlight for Jacob Aae Mikkelsen who continues Burt Beckwith original “this week in Grails” column, in the form of the <a href="http://grydeske.net/news/show/22">Grails diary</a>. This “Groovy Weekly” column obviously overlaps a bit with the Grails diary, but the Grails diary has a particular focus on Grails which you might be interested in, especially with regards to the latest news regarding the Grails plugins ecosystem. Be sure to check it out if you’re developing with Grails.</p>
<p>Last but not least, Trisha Gee published three interesting articles on the Spock testing framework:</p>
<ul>
<li><a href="http://mechanitis.blogspot.co.uk/2013/07/spock-is-awesome-seriously-simplified.html">mocking with Spock</a></li>
<li><a href="http://mechanitis.blogspot.co.uk/2013/07/spock-passes-next-test-painless-stubbing.html">stubbing with Spock</a></li>
<li><a href="http://mechanitis.blogspot.com.es/2013/12/spock-data-driven-testing.html">data-driven testing with Spock</a></li>
</ul>
<h2 id="groovy--grails-exchange-2013-special">Groovy / Grails eXchange 2013 special</h2>
<p>On the 12th and 13th of December, took place the <a href="http://skillsmatter.com/event-details/home/groovy-grails-exchange-2013">Groovy / Grails eXchance conference</a>, in London, UK, organized by the fine folks of <a href="http://skillsmatter.com/">SkillsMatter</a>, gathering the Groovy ecosystem crowd, to speak about Groovy, Grails, Gradle, Ratpack, and more.</p>
<p>What’s special about this conference is that it’s been SkillsMatter which organized the first ever conference on the Groovy ecosystem back in 2007! Also, what’s double nice is that they record everything and that the presentations are available in matter of hours at no cost. So let me guide you through all the presentations that took place at the conference:</p>
<h3 id="first-day">First day</h3>
<ul>
<li><a href="http://skillsmatter.com/podcast/home/keynote-guillaume-laforge">The Groovy update</a> by Guillaume Laforge</li>
<li><a href="http://skillsmatter.com/podcast/home/making-java-apis-groovy">Making Java APIs Groovy</a> by Cédric Champeau</li>
<li><a href="http://skillsmatter.com/podcast/home/devqa-make-your-testers-happy-with-groovy-spock-and-geb">DevQA, make your testers happier with Groovy, Spock, and Geb</a> by Alvaro Sanchez Mariscal</li>
<li><a href="http://skillsmatter.com/podcast/home/modern-groovy-enterprise-stack">Modern Groovy enterprise stack</a> by Marcin Edmann</li>
<li><a href="http://skillsmatter.com/podcast/home/creating-architectural-flexibility-using-groovy">Creating architectural flexibility with Groovy</a> by David Dawson</li>
<li><a href="http://skillsmatter.com/podcast/home/ratpack-a-toolkit-for-jvm-web-applications">Ratpack, a toolkit for JVM web applications</a> by Luke Daley</li>
<li><a href="http://skillsmatter.com/podcast/home/nosql-with-grails">NoSQL with Grails</a> by Joseph Nusairat</li>
<li><a href="http://skillsmatter.com/podcast/home/developing-spi-applications-using-grails-and-angularjs">Developing single-page applications with Grails and AngularJS</a> by Alvaro Sanchez Mariscal</li>
<li><a href="http://skillsmatter.com/podcast/home/polyglot-programming-in-grails-2">Polyglot programming in Grails</a> by Jeff Brown</li>
<li><a href="http://skillsmatter.com/podcast/home/is-groovy-static-or-dynamic">Is Groovy static or dynamic</a> by Russel Winder</li>
<li><a href="http://skillsmatter.com/podcast/home/message-driven-architecture-in-grails">Message driven architecture in Grails</a> by Dan Woods</li>
</ul>
<h2 id="second-day">Second day</h2>
<ul>
<li><a href="http://skillsmatter.com/podcast/home/road-to-grails-3-0">The road to Grails 3.0</a> by Graeme Rocher</li>
<li><a href="http://skillsmatter.com/podcast/home/build-grails-applications-with-gradle">Build Grails applications with Gradle</a> by Luke Daley</li>
<li><a href="http://skillsmatter.com/podcast/home/metaprogramming-with-groovy-compiler">Metaprogramming with the Groovy runtime</a> by Jeff Brown</li>
<li><a href="http://skillsmatter.com/podcast/home/restfully-async-with-grails-2-3">Restful async with Grails 2.3</a> by Graeme Rocher</li>
<li><a href="http://skillsmatter.com/podcast/home/open-source-and-you">Open Source and you</a> by Peter Ledbrook</li>
<li><a href="http://skillsmatter.com/podcast/home/reactor">Reactor, a foundation for asynchronous applications on the JVM</a> by Stéphane Maldini</li>
<li><a href="http://skillsmatter.com/podcast/home/groovy-for-system-administrators">Groovy for system administrators</a> by Dan Woods</li>
<li><a href="http://skillsmatter.com/podcast/home/building-lightning-fast-rest-services-with-dropwizard-and-groovy">Building lightning fast REST services with Dropwizard and Groovy</a> by Tomas Lin</li>
<li><a href="http://skillsmatter.com/podcast/home/how-gradle-saved-the-day-at-a-major-company">How Gradle saved the day at a major company</a> by Joseph Nusairat</li>
<li><a href="http://skillsmatter.com/podcast/home/application-architecture-in-groovy-4945">Application architecture in Groovy</a> by Dan Woods</li>
<li><a href="http://skillsmatter.com/podcast/home/groovy-micro-services-with-spring-boot">Groovy micro services with Spring Boot</a> by David Dawson</li>
</ul>
<h2 id="other-presentations">Other presentations</h2>
<p>Other presentations are also available from other conferences, in particular, InfoQ has been publishing SpringOne2GX 2013 and GR8Conf US talks on a regular basis. Here’s a recent selection of talks they released:</p>
<h3 id="springone2gx">SpringOne2GX</h3>
<ul>
<li>Core Groovy committers Paul King &amp; Guillaume Laforge gave a presentation on <a href="http://www.infoq.com/presentations/groovy-dsl-mars">creating Domain-Specific Languages in Groovy that developers can actually use</a></li>
<li>Burt Beckwith discusses the security risks web applications may face (XSS, CRSF, SQL injection) and the libraries and plugins that developers can use to <a href="http://www.infoq.com/presentations/security-grails-apps">secure their Grails applications</a></li>
<li>Baruch Sadogursky <a href="http://www.infoq.com/presentations/grails-search-test-tools">overviews and compares search and testing tools available to Grails developers</a></li>
</ul>
<h3 id="gr8conf-us">GR8Conf US</h3>
<ul>
<li><a href="http://www.infoq.com/presentations/groovy-tips">Advanced Groovy tips &amp; tricks</a> by Ken Kousen, author of <a href="http://www.manning.com/kousen/">Making Java Groovy</a></li>
<li><a href="http://www.infoq.com/presentations/groovy-adfm">Programming in the Cloud - Groovy as an Extension Language for Oracle ADFm</a> by Jim Driscoll</li>
<li>Luke Daley presents the <a href="http://www.infoq.com/presentations/ratpack">Ratpack micro web framework</a></li>
<li>Peter Ledbrook speaks about <a href="http://www.infoq.com/presentations/grails-app-architecture">application architectures with Grails</a></li>
</ul>
<h3 id="øredev">Øredev</h3>
<p>Andrés Almiray shows the <a href="http://vimeo.com/79868215">functional aspects of the Groovy</a> programming language in this presentation from Øredev.</p>
<p>On that functional theme, you can also have a look at:</p>
<ul>
<li>Paul King’s <a href="http://fr.slideshare.net/paulk_asert/functional-groovy">Functional Groovy</a> at SpringOne2GX</li>
<li>Guillaume Laforge’s <a href="https://speakerdeck.com/glaforge/functional-groovy">Functional Groovy</a> at Scala.IO (slides available, but video not yet published)</li>
</ul>
<h3 id="jax-london">JAX London</h3>
<p>Guillaume Laforge talks about <a href="http://jaxenter.com/guillaume-laforge-what-makes-groovy-groovy-49157.html">what makes Groovy groovy</a>. He also gave an updated presentation at Devoxx which is <a href="http://www.parleys.com/play/52a10c8be4b039ad2298ca76/chapter1/about">published on Parleys</a>, but it’s viewable for a fee (unless you attended the Devoxx conference) until it’s released publicly, but the <a href="https://speakerdeck.com/glaforge/what-makes-groovy-groovy-devoxx-2013">slides</a> are available on speakerdec.</p>
<h2 id="code-snippets">Code snippets</h2>
<ul>
<li><a href="https://gist.github.com/timyates/7823738">Reactive Rock Paper Scisors</a> by Tim Yates, using Groovy and RxJava</li>
<li>Marcin Erdmann <a href="https://github.com/geb/geb/tree/master/doc/site">developed</a> the new version of the <a href="http://www.gebish.org/">Geb website</a> with Ratpack, deployed on Heroku</li>
<li>Luke Daley published a snippet of a <a href="https://gist.github.com/alkemist/7943781">self contained Groovy script to bootstrap a Ratpack server</a></li>
</ul>
<h2 id="tweets">Tweets</h2>
<ul>
<li>Guillaume Laforge tweeted about Graeme Rocher’s <a href="https://twitter.com/glaforge/status/411426080407711744">4 R&rsquo;s of Grails controllers: return, render, redirect and now respond for Rest</a></li>
</ul>
<h2 id="mailing-list-discussions">Mailing-list discussions</h2>
<p>Cédric Champeau shows the current work in progress around <a href="http://groovy.329449.n5.nabble.com/Closure-parameter-type-inference-td5717804.html">providing further type inference for methods taking closure arguments</a>, as closures don&rsquo;t really offer a signature (a return type thanks to generics, yes, but no way to specify the type of the arguments).</p>
<p>Example of methods that we&rsquo;ll be able to type checked:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@TypeChecked</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">void</span> <span style="color:#06287e">foo</span><span style="color:#666">(</span>List list<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    println list<span style="color:#666">.</span><span style="color:#4070a0">collect</span> <span style="color:#666">{</span> it<span style="color:#666">.</span><span style="color:#4070a0">toUpperCase</span><span style="color:#666">()</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Before, the type checker would complain that it didn’t know the type of the implicit ‘it’, but by decorating the collect() method implementation with new dedicated annotations, it’s possible to instruct the type checker about the type of the closure parameters.</p>
<h2 id="books">Books</h2>
<p>The <a href="https://leanpub.com/groovy-goodness-notebook/">Groovy Goodness notebook</a> from MrHaki has been <a href="http://mrhaki.blogspot.fr/2013/12/groovy-goodness-notebook-updated-with.html">udpated with the Groovy 2.2 features</a>.</p>
<h2 id="events">Events</h2>
<ul>
<li>The <a href="http://cfp.gr8conf.org/login/auth">Call for Papers</a> for the GR8Conf Europe (Copenhagen, Denmark, on June 2nd-4th 2014) and GR8Conf US (Minneapolis, USA, on July 28th-29th 2014) conferences is now open</li>
<li>The Call for Papers for the <a href="http://greach.es/">Greach</a> conference (Madrid, Spain, on March 28th and 29th 2014) is also open, till January 31st</li>
</ul>
<h2 id="closing-thoughts">Closing thoughts</h2>
<p>Again, best wishes from the Groovy team for the holidays, and we’re looking forward to your feedback on this “Groovy Weekly” column, and to your contribution through the form: <a href="http://bit.ly/groovyweekly">http://bit.ly/groovyweekly</a></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.2 released</title><link>https://glaforge.dev/posts/2013/11/18/groovy-2-2-released/</link><pubDate>Mon, 18 Nov 2013 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2013/11/18/groovy-2-2-released/</guid><description>&lt;p>This is with great pleasure that the Groovy team is announcing today the &lt;strong>release of Groovy 2.2&lt;/strong>, the latest version of the Groovy programming language.&lt;/p>
&lt;p>Groovy 2.2 features:&lt;/p>
&lt;ul>
&lt;li>Implicit closure coercion&lt;/li>
&lt;li>&lt;code>@Memoized&lt;/code> AST transformation for methods&lt;/li>
&lt;li>Define base script classes with an annotation&lt;/li>
&lt;li>New &lt;code>DelegatingScript&lt;/code> base class for scripts&lt;/li>
&lt;li>New &lt;code>@Log&lt;/code> variant for the Log4j2 logging framework&lt;/li>
&lt;li>&lt;code>@DelegatesTo&lt;/code> with generics type tokens&lt;/li>
&lt;li>Precompiled type checking extensions&lt;/li>
&lt;li>Groovysh enhancements&lt;/li>
&lt;li>Bintray&amp;rsquo;s JCenter repository&lt;/li>
&lt;li>OSGi manifests for the “invoke dynamic” JARs&lt;/li>
&lt;li>And other minor bug fixes, enhancements and performance improvements&lt;/li>
&lt;/ul>
&lt;p>You can read more &lt;a href="http://bit.ly/g22notes">detailed release notes&lt;/a>﻿ as well as have a look at all the &lt;a href="http://bit.ly/g22jira">JIRA issues fixed&lt;/a>﻿.&lt;/p></description><content:encoded>
<![CDATA[<p>This is with great pleasure that the Groovy team is announcing today the <strong>release of Groovy 2.2</strong>, the latest version of the Groovy programming language.</p>
<p>Groovy 2.2 features:</p>
<ul>
<li>Implicit closure coercion</li>
<li><code>@Memoized</code> AST transformation for methods</li>
<li>Define base script classes with an annotation</li>
<li>New <code>DelegatingScript</code> base class for scripts</li>
<li>New <code>@Log</code> variant for the Log4j2 logging framework</li>
<li><code>@DelegatesTo</code> with generics type tokens</li>
<li>Precompiled type checking extensions</li>
<li>Groovysh enhancements</li>
<li>Bintray&rsquo;s JCenter repository</li>
<li>OSGi manifests for the “invoke dynamic” JARs</li>
<li>And other minor bug fixes, enhancements and performance improvements</li>
</ul>
<p>You can read more <a href="http://bit.ly/g22notes">detailed release notes</a>﻿ as well as have a look at all the <a href="http://bit.ly/g22jira">JIRA issues fixed</a>﻿.</p>
<p>Then <a href="http://bit.ly/g22dl">download Groovy 2.2</a>﻿ while it&rsquo;s hot!</p>
<p>Thanks a lot to all the users, contributors, developers who contributed to that release!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Interview on JAXenter about Groovy</title><link>https://glaforge.dev/posts/2013/11/05/interview-on-jaxenter-about-groovy/</link><pubDate>Tue, 05 Nov 2013 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2013/11/05/interview-on-jaxenter-about-groovy/</guid><description>&lt;p>Following up my &lt;a href="https://glaforge.dev/talks/2013/11/04/groovy-presentations-at-conferences/">presentations from JAX London&lt;/a>, I was interviewed about Groovy for JAXenter, with questions about what Groovy brings to Java developers, the recent surge in popularity as &lt;a href="https://glaforge.dev/posts/2013/10/11/groovy-enters-top-20-of-the-tiobe-language-index/">Groovy broke the TIOBE index top 20 languages&lt;/a>, my favorite projects in the Groovy ecosystem.&lt;/p>
&lt;p>&lt;a href="http://jaxenter.com/a-one-to-one-with-the-groovy-project-manager-48667.html">&lt;figure>
&lt;a href="#img-2f68b69db770b5928d51bdbe7e559cd0">
&lt;img src="https://glaforge.dev/img/misc/jaxenter-interview.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-2f68b69db770b5928d51bdbe7e559cd0">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/jaxenter-interview.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/a>&lt;/p></description><content:encoded>
<![CDATA[<p>Following up my <a href="https://glaforge.dev/talks/2013/11/04/groovy-presentations-at-conferences/">presentations from JAX London</a>, I was interviewed about Groovy for JAXenter, with questions about what Groovy brings to Java developers, the recent surge in popularity as <a href="https://glaforge.dev/posts/2013/10/11/groovy-enters-top-20-of-the-tiobe-language-index/">Groovy broke the TIOBE index top 20 languages</a>, my favorite projects in the Groovy ecosystem.</p>
<p><a href="http://jaxenter.com/a-one-to-one-with-the-groovy-project-manager-48667.html"><figure>
  <a href="#img-2f68b69db770b5928d51bdbe7e559cd0">
    <img src="/img/misc/jaxenter-interview.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-2f68b69db770b5928d51bdbe7e559cd0">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/jaxenter-interview.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</a></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy presentations at conferences</title><link>https://glaforge.dev/talks/2013/11/04/groovy-presentations-at-conferences/</link><pubDate>Mon, 04 Nov 2013 00:00:00 +0100</pubDate><guid>https://glaforge.dev/talks/2013/11/04/groovy-presentations-at-conferences/</guid><description>&lt;p>Last week, I was in London for the &lt;a href="http://jaxlondon.com/">JAX London 2013&lt;/a> conference. I gave a presentation on what makes the Groovy programming language&amp;hellip; groovy! It&amp;rsquo;s essentially an introduction to Groovy, its language constructs, its ecosystem, etc. It&amp;rsquo;s not focusing particularly on new features or on metaprogramming, but tries to bring the audience to the boilerplate killer that is Groovy. I&amp;rsquo;ve posted the slides on SpeakerDeck and I embed them below:&lt;/p>
&lt;p>I also had the chance to do a special speech at the London Java Community night during the conference about some (obvious) advice I could give developers, that I learnt through my working in Open Source, on the Groovy project, etc, illustrated with some quotes that I like. Again, slides embedded below:&lt;/p></description><content:encoded>
<![CDATA[<p>Last week, I was in London for the <a href="http://jaxlondon.com/">JAX London 2013</a> conference. I gave a presentation on what makes the Groovy programming language&hellip; groovy! It&rsquo;s essentially an introduction to Groovy, its language constructs, its ecosystem, etc. It&rsquo;s not focusing particularly on new features or on metaprogramming, but tries to bring the audience to the boilerplate killer that is Groovy. I&rsquo;ve posted the slides on SpeakerDeck and I embed them below:</p>
<p>I also had the chance to do a special speech at the London Java Community night during the conference about some (obvious) advice I could give developers, that I learnt through my working in Open Source, on the Groovy project, etc, illustrated with some quotes that I like. Again, slides embedded below:</p>
<p>And last but not least, I also wanted to mention that my <a href="https://speakerdeck.com/glaforge/lift-off-with-groovy-2">presentation on the Groovy 2.x line</a> from SpringOne2GX had just been posted to InfoQ.</p>
<p><a href="http://www.infoq.com/presentations/groovy-2-1-2gx"><figure>
  <a href="#img-f633ecca9a9af48a575d4e0502fb6dbc">
    <img src="https://glaforge.appspot.com/media/infoq-liftoff-groovy-talk.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-f633ecca9a9af48a575d4e0502fb6dbc">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://glaforge.appspot.com/media/infoq-liftoff-groovy-talk.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</a></p>
<script async class="speakerdeck-embed" data-id="b499eea0213b013151e34a4948aa6526" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Functional Groovy presentation</title><link>https://glaforge.dev/talks/2013/10/27/functional-groovy-presentation/</link><pubDate>Sun, 27 Oct 2013 00:00:00 +0200</pubDate><guid>https://glaforge.dev/talks/2013/10/27/functional-groovy-presentation/</guid><description>&lt;p>I&amp;rsquo;ve had the pleasure of being invited at the &lt;a href="http://scala.io/">Scala.IO&lt;/a> conference to speak about the functional aspects of Groovy, as the conference was also open to other functional language.&lt;/p>
&lt;p>The Groovy programming language, since its inception, has always been pretty functional, in particular with the cornerstone that our Closures represent to serve as first-class citizen functions in the language.&lt;/p>
&lt;p>Inspired by other nice talks from Andrés, Paul, Arturo, I&amp;rsquo;ve created my own deck that I&amp;rsquo;ve posted on SpeakerDeck and that I embed below. Please be sure to have a look at the &amp;ldquo;resources&amp;rdquo; section of that deck, for further pointers on functional programming with Groovy.&lt;/p></description><content:encoded>
<![CDATA[<p>I&rsquo;ve had the pleasure of being invited at the <a href="http://scala.io/">Scala.IO</a> conference to speak about the functional aspects of Groovy, as the conference was also open to other functional language.</p>
<p>The Groovy programming language, since its inception, has always been pretty functional, in particular with the cornerstone that our Closures represent to serve as first-class citizen functions in the language.</p>
<p>Inspired by other nice talks from Andrés, Paul, Arturo, I&rsquo;ve created my own deck that I&rsquo;ve posted on SpeakerDeck and that I embed below. Please be sure to have a look at the &ldquo;resources&rdquo; section of that deck, for further pointers on functional programming with Groovy.</p>
<p>The talk has been recorded and should be published on InfoQ sometime soon.</p>
<script async class="speakerdeck-embed" data-id="b499eea0213b013151e34a4948aa6526" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Interview about Groovy's popularity boost</title><link>https://glaforge.dev/posts/2013/10/15/interview-about-groovy-s-popularity-boost/</link><pubDate>Tue, 15 Oct 2013 02:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2013/10/15/interview-about-groovy-s-popularity-boost/</guid><description>&lt;p>I was interviewed by Darryl Taft from eWeek yesterday about the Groovy programming language&amp;rsquo;s recent popularity boost. You can read the two-page long interview here:&lt;br />
&lt;a href="http://www.eweek.com/developer/groovy-programming-language-sees-major-boost-in-popularity.html">Groovy programming language sees major boost in popularity&lt;/a>&lt;/p>
&lt;p>You&amp;rsquo;ll certainly be interested in the answers to some of the questions, in particular who&amp;rsquo;s using Groovy, or for which use cases Groovy is being used:&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>&amp;mdash; Why do you think Groovy has gained in popularity over the last year?&lt;/strong>&lt;/p></description><content:encoded>
<![CDATA[<p>I was interviewed by Darryl Taft from eWeek yesterday about the Groovy programming language&rsquo;s recent popularity boost. You can read the two-page long interview here:<br />
<a href="http://www.eweek.com/developer/groovy-programming-language-sees-major-boost-in-popularity.html">Groovy programming language sees major boost in popularity</a></p>
<p>You&rsquo;ll certainly be interested in the answers to some of the questions, in particular who&rsquo;s using Groovy, or for which use cases Groovy is being used:</p>
<blockquote>
<p><strong>&mdash; Why do you think Groovy has gained in popularity over the last year?</strong></p>
<p>Groovy&rsquo;s widely used in the industry as the 1.7 million downloads a year can attest, but there&rsquo;s been an interesting surge in popularity recently. In particular, as you could see at SpringOne2GX in September, Groovy was nicely featured in the Pivotal&rsquo;s<a href="http://spring.io/">Spring IO</a> platform for example in the upcoming Spring Framework 4.0 version, in <a href="https://spring.io/blog/2013/08/06/spring-boot-simplifying-spring-for-everyone">Spring Boot</a>, or <a href="https://spring.io/blog/2013/05/13/reactor-a-foundation-for-asynchronous-applications-on-the-jvm">Reactor</a> and other projects. There&rsquo;s a lot of buzz around the Groovy ecosystem: with the <a href="http://grails.org/">Grails</a> web framework ongoing growth, or the <a href="http://www.gradle.org/">Gradle</a> build automation system that is being used by <a href="http://tools.android.com/tech-docs/new-build-system">Google for the building Android application</a>.</p>
<p><strong>&mdash; What makes Groovy an attractive language to use?</strong></p>
<p>Groovy&rsquo;s main goals has always been to simplify the life of developers, and particularly Java developers.</p>
<p>Its syntax is based off of the Java language itself, so it&rsquo;s very straightforward and intuitive to learn, but at the same time offers various shortcuts and APIs to make developers much more productive, help them write more concise and more readable code, easier to maintain and evolve in the long run.</p>
<p>It&rsquo;s also the alternative language on the Java Virtual Machine that offers the best and most seamless integration and interoperability with the Java platform, as developers don&rsquo;t need to convert from one system to the other as Groovy classes are plain Java classes and vice versa.</p>
<p>Groovy&rsquo;s overall well suited for many situations, for writing small scripts to building more complex applications, for designing Domain-Specific Languages (DSLs) for business rules, etc.</p>
<p><strong>&mdash; What types of applications is Groovy most suited for?</strong></p>
<p>There are different use cases for applying Groovy in your projects.<br />
Often Groovy is introduced in companies and projects thanks to the nice testing tools it offers and how readable tests can become thanks to them — for example, with the <a href="http://docs.spockframework.org/en/latest/">Spock testing framework</a>. Other shops adopt <a href="http://www.gradle.org/">Gradle</a> for building complex applications where other tools like Ant or Maven show their weaknesses.</p>
<p>Groovy is also often used for &ldquo;scripting&rdquo; or customizing applications through extension points featuring Groovy scripts.</p>
<p>The Internet of Things world is using Groovy for scripting home automation, for interacting with the various nodes of the mesh network (<a href="http://www.smartthings.com/">SmartThings</a> and <a href="https://www.carriots.com/">Carriots</a> are integrating Groovy for that purpose).<br />
You can also customize the Jenkins continuous integration platform with Groovy, or interact with the ElasticSearch search engine with a Groovy API, etc.</p>
<p>Another interesting use case is for business rules.<br />
I already briefly mentioned business rules and DSLs, but it&rsquo;s one of the key use cases for Groovy.</p>
<p>I&rsquo;ve seen several financial institutions, insurance companies, travel or energy companies use Groovy for describing their business rules — actuaries defining rules for loan grants, scientists creating scientific simulations (nuclear risks, disease evolutions, etc.), travel agents customizing booking processes, and more.</p>
<p>The easy Groovy syntax plus its ability to write almost plain English sentences which are valid Groovy statements makes it simple to write readable business rules.</p>
<p>Of course, also, beside just &ldquo;integrating&rdquo; Groovy in existing apps to extend their capabilities or writing business rules, you can also build full blown applications totally in Groovy, like when you build a big web application with the Grails web stack.</p>
<p><strong>&mdash; Are there any big Groovy applications out there, or any major companies using it?</strong></p>
<p>I was recently asked a similar question on Quora, where you can read my more <a href="http://www.quora.com/Whos-using-Groovy-in-production">detailed answer</a>.</p>
<p>Above, I&rsquo;ve listed a few of the key use cases where Groovy is often used, and in that question on Quora I&rsquo;m giving a few examples of those scenarios.</p>
<p>To name a few companies using Groovy:</p>
<ul>
<li>
<p><strong>Netflix</strong> is using Groovy in their <a href="http://netflix.github.io/asgard/">Asgard</a> cloud automation platform (a case study will soon be published on Pivotal&rsquo;s blog by the way), as well as for other handy libraries like <a href="http://techblog.netflix.com/2013/09/glisten-groovy-way-to-use-amazons.html">Glisten</a> to interact with Amazon&rsquo;s Simple Workflow Service.</p>
</li>
<li>
<p><strong>Google</strong> have moved the <strong>Android</strong> application build over to Gradle which uses Groovy as the build language, so all Android developers will soon be using Groovy for building their Android applications</p>
</li>
<li>
<p><strong>LinkedIn</strong> developed <a href="http://pongasoft.github.io/glu/docs/latest/html/index.html">Glu</a> (their open source deployment and monitoring platform) in Groovy, but they are also using Grails web applications for recruiters using the LinkedIn platform and profile database</p>
</li>
<li>
<p>Various financial institutions like <strong>JPMorgan</strong>, <strong>MasterCard</strong>, <strong>Crédit Suisse</strong>, <strong>Fanny Mae</strong>, <strong>Mutual of Omaha</strong></p>
</li>
<li>
<p>The <strong>European Patent Office</strong> is using Groovy for collecting patents from all over the world, extract / transform all those patent formats in some kind of pivot formats, all that with Groovy<br />
And many more&hellip; but the above are some of the more famous names that I know about.</p>
</li>
</ul>
<p><strong>&mdash; What are you doing with Groovy these days?</strong></p>
<p>These days, we are ironing out the latest details of the Groovy 2.2 final release.</p>
<p>We are also working on future versions of Groovy at the same time, and investigating and experimenting other areas where Groovy could offer some nice productivity boosts for developers (think simplifying Android development, big data manipulation and analyses, etc.)</p></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Release candidate for Groovy 2.2</title><link>https://glaforge.dev/posts/2013/10/15/release-candidate-for-groovy-2-2/</link><pubDate>Tue, 15 Oct 2013 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2013/10/15/release-candidate-for-groovy-2-2/</guid><description>&lt;p>Yesterday, the Groovy programming language team released the release candidate for Groovy 2.2, as well as a bug-fix release of Groovy 2.1.8.&lt;/p>
&lt;p>Here&amp;rsquo;s the announcement I sent on the various communication channels:&lt;/p>
&lt;blockquote>
&lt;p>The Groovy team is happy to announce the release of the release candidate of Groovy 2.2, as well as a bug-fix release for Groovy 2.1.8.&lt;/p>
&lt;p>As you can guess with this release candidate, the final version of Groovy 2.2 is fast approaching, and we&amp;rsquo;d be happy to get as much feedback on this release as possible, to squash potential bugs before the general availability of 2.2. So please be sure to test your applications with this release candidate.&lt;/p></description><content:encoded>
<![CDATA[<p>Yesterday, the Groovy programming language team released the release candidate for Groovy 2.2, as well as a bug-fix release of Groovy 2.1.8.</p>
<p>Here&rsquo;s the announcement I sent on the various communication channels:</p>
<blockquote>
<p>The Groovy team is happy to announce the release of the release candidate of Groovy 2.2, as well as a bug-fix release for Groovy 2.1.8.</p>
<p>As you can guess with this release candidate, the final version of Groovy 2.2 is fast approaching, and we&rsquo;d be happy to get as much feedback on this release as possible, to squash potential bugs before the general availability of 2.2. So please be sure to test your applications with this release candidate.</p>
<p>For both releases, we&rsquo;ve made a small update to the default Grab configuration which is to use Bintray&rsquo;s JCenter repository as the first in the chain of resolvers, as Bintray&rsquo;s JCenter repository is noticeably faster and more responsive than Maven Central, offers dependencies always with their checksums, and stores and caches dependencies it wouldn&rsquo;t have for faster delivery the next time a dependency is required. This should make your scripts relying on @Grab faster when downloading dependencies for the first time.</p>
<p>An interesting feature in the release candidate only is the ability for scripts to define their base script class. All scripts usually extend the groovy.lang.Script abstract class, but it&rsquo;s possible to set up our own base script class extending Script through CompilerConfiguration. A new AST transformation is introduced in Groovy 2.2 which allows you to define the base script class as follows:</p></blockquote>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.transform.BaseScript</span>  
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">abstract</span> <span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">DeclaredBaseScript</span> <span style="color:#007020;font-weight:bold">extends</span> Script <span style="color:#666">{</span> 
</span></span><span style="display:flex;"><span>    <span style="color:#902000">int</span> meaningOfLife <span style="color:#666">=</span> <span style="color:#40a070">42</span> 
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>  
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@BaseScript</span> DeclaredBaseScript baseScript  
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> meaningOfLife <span style="color:#666">==</span> <span style="color:#40a070">42</span>
</span></span></code></pre></div><p>Again for 2.2, a new @Log variant has been added to support Log4j2, with the @Log4j2 AST transformation:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.util.logging.Log4j2</span>  
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Log4j2</span> <span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">MyClass</span> <span style="color:#666">{</span> 
</span></span><span style="display:flex;"><span>    <span style="color:#902000">void</span> <span style="color:#06287e">doSomething</span><span style="color:#666">()</span> <span style="color:#666">{</span> 
</span></span><span style="display:flex;"><span>        log<span style="color:#666">.</span><span style="color:#4070a0">info</span> <span style="color:#4070a0">&#34;did something groovy today!&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span> 
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>For more details on those two releases, please have a look at the release notes on JIRA:</p>
<ul>
<li><a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=19570">Groovy 2.1.8 JIRA release notes</a></li>
<li><a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=19569">Groovy 2.2.0-rc-1 JIRA release notes</a></li>
</ul>
<p>Go download Groovy 2.1.8 and test drive 2.2.0-rc-1 while they&rsquo;re hot, in the <a href="http://groovy.codehaus.org/Download?nc">download area</a>!</p>
<p>Thanks a lot for your contributions to those releases, in terms of bug reports, discussions on the mailing-lists, code contributions and pull requests!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy enters top 20 of the TIOBE language index</title><link>https://glaforge.dev/posts/2013/10/11/groovy-enters-top-20-of-the-tiobe-language-index/</link><pubDate>Fri, 11 Oct 2013 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2013/10/11/groovy-enters-top-20-of-the-tiobe-language-index/</guid><description>&lt;p>For the first time, &lt;a href="http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html">Groovy enters the top 20 of the TIOBE programming language index&lt;/a>, as you can see in the table below:&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-148109cf69ebed061a10796456937bcc">
&lt;img src="https://glaforge.dev/img/misc/TIOBE&amp;#43;2.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-148109cf69ebed061a10796456937bcc">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/TIOBE&amp;#43;2.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>As the headline and first paragraph indicate:&lt;/p>
&lt;blockquote>
&lt;p>TIOBE Programming Community Index for October 2013&lt;br />
October Headline: Lightweight Java language Groovy enters top 20&lt;br />
Programming language&lt;/p>
&lt;p>Groovy enters the top 20 for the first time. Groovy is an object-oriented scripting language that runs on the Java Virtual Machine. It is fully compatible with Java, making it easy to combine Java programs with Groovy. There is quite some competition in this field of lightweight languages that fit together with Java. Groovy beats other well-known JVM languages such as Scala (#36), JavaFX Script (#41) and Clojure (#76). Let&amp;rsquo;s see whether it can keep its top 20 position the next few months.&lt;/p></description><content:encoded>
<![CDATA[<p>For the first time, <a href="http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html">Groovy enters the top 20 of the TIOBE programming language index</a>, as you can see in the table below:</p>
<p><figure>
  <a href="#img-148109cf69ebed061a10796456937bcc">
    <img src="/img/misc/TIOBE&#43;2.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-148109cf69ebed061a10796456937bcc">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/TIOBE&#43;2.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>As the headline and first paragraph indicate:</p>
<blockquote>
<p>TIOBE Programming Community Index for October 2013<br />
October Headline: Lightweight Java language Groovy enters top 20<br />
Programming language</p>
<p>Groovy enters the top 20 for the first time. Groovy is an object-oriented scripting language that runs on the Java Virtual Machine. It is fully compatible with Java, making it easy to combine Java programs with Groovy. There is quite some competition in this field of lightweight languages that fit together with Java. Groovy beats other well-known JVM languages such as Scala (#36), JavaFX Script (#41) and Clojure (#76). Let&rsquo;s see whether it can keep its top 20 position the next few months.</p></blockquote>
<p>There was a lot of buzz last month around Groovy being an important theme of the Pivotal <a href="https://spring.io/">Spring.IO</a> stack, while we were at the <a href="http://springone2gx.com">SpringOne2GX conference</a>, that this buzz certainly helped increase Groovy&rsquo;s position in this index.</p>
<p>A few words of caution, however, as I don&rsquo;t think we can really read too much into those numbers, as the ranking algorithm is essentially just ranking the buzz or hype around a given language at a given point in time.</p>
<p>From a given month to another, Groovy moved between 20th something to 50th, then back in the 30th&hellip; So it&rsquo;s not really reliable or meaningful. It&rsquo;s not really able to compare real actual usage of a programming language, otherwise it wouldn&rsquo;t move so much from month to month. We&rsquo;ll see if we hold that position next month!</p>
<p>But with 1.7 million downloads of Groovy last year, Groovy is no small player in the field!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Second beta for Groovy 2.2</title><link>https://glaforge.dev/posts/2013/09/06/second-beta-for-groovy-2-2/</link><pubDate>Fri, 06 Sep 2013 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2013/09/06/second-beta-for-groovy-2-2/</guid><description>&lt;p>The Groovy team is happy to announce the &lt;strong>releases of the second beta of Groovy 2.2&lt;/strong>, along with a &lt;strong>bug fix release of Groovy 2.1.7&lt;/strong>.&lt;/p>
&lt;p>We&amp;rsquo;re close to moving towards RC mode for the upcoming Groovy 2.2 release, and we&amp;rsquo;d be happy to &lt;strong>hear about your feedback about this new version&lt;/strong> when used in your projects. Please have a moment to test it against your code, and tell us if you notice any problem or regression.&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy team is happy to announce the <strong>releases of the second beta of Groovy 2.2</strong>, along with a <strong>bug fix release of Groovy 2.1.7</strong>.</p>
<p>We&rsquo;re close to moving towards RC mode for the upcoming Groovy 2.2 release, and we&rsquo;d be happy to <strong>hear about your feedback about this new version</strong> when used in your projects. Please have a moment to test it against your code, and tell us if you notice any problem or regression.</p>
<p>Groovy 2.2.0-beta-2 is pretty much feature complete by now, with only a few minor improvements, and both 2.2.0-beta-2 and 2.1.7 contain a great deal of bug fixes around our static type checker and compilation support.</p>
<p>You can have a look at the JIRA release notes for the details:</p>
<ul>
<li><a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=19430">Groovy 2.1.7</a></li>
<li><a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=19431">Groovy 2.2.0-beta-2</a></li>
</ul>
<p>Those two Groovy versions are available in the <a href="http://groovy.codehaus.org/Download">download area</a> of the website.</p>
<p>Thanks a lot to all those who contributed to this release, and we&rsquo;re looking forward to hearing your feedback about the upcoming 2.2!</p>
<p>We&rsquo;d also be happy to see you at next week&rsquo;s <a href="http://www.springone2gx.com/conference/santa_clara/2013/09/register">SpringOne2GX conference</a> if you&rsquo;re around! So don&rsquo;t hesitate to come and say hi to the Groovy team members who will be present — and there are quite a few of us there!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>GPars 1.1 is out</title><link>https://glaforge.dev/posts/2013/07/25/gpars-1-1-is-out/</link><pubDate>Thu, 25 Jul 2013 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2013/07/25/gpars-1-1-is-out/</guid><description>&lt;p>&lt;a href="http://www.jroller.com/vaclav/">Václav Pech&lt;/a> just announced the release of GPars 1.1, the multi-paradigm concurrency and parallel Groovy-friendly toolkit:&lt;/p>
&lt;blockquote>
&lt;p>The GA release of GPars 1.1.0 has just been published and is ready for you to grab. It brings gradual improvements into dataflow as well as a few other domains. Some highlights:&lt;/p>
&lt;ul>
&lt;li>&lt;code>LazyDataflowVariable&lt;/code> added to allow for lazy asynchronous values&lt;/li>
&lt;li>Timeout for Selects&lt;/li>
&lt;li>Added a Promise-based API for value selection through the Select class&lt;/li>
&lt;li>Enabled listening for bind errors on DataflowVariables&lt;/li>
&lt;li>Minor API improvement affecting Promise and DataflowReadChannel&lt;/li>
&lt;li>Protecting agent&amp;rsquo;s blocking methods from being called from within commands&lt;/li>
&lt;li>Updated to the latest 0.7.0 GA version of Multiverse&lt;/li>
&lt;li>Migrated to Groovy 2.0&lt;/li>
&lt;li>Used &lt;code>@CompileStatic&lt;/code> where appropriate&lt;/li>
&lt;li>A few bug fixes&lt;/li>
&lt;/ul>
&lt;p>You can &lt;a href="http://gpars.codehaus.org/Download">download&lt;/a> GPars 1.1.0 directly or &lt;a href="http://gpars.codehaus.org/Integration">grab it from the maven repo&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://www.jroller.com/vaclav/">Václav Pech</a> just announced the release of GPars 1.1, the multi-paradigm concurrency and parallel Groovy-friendly toolkit:</p>
<blockquote>
<p>The GA release of GPars 1.1.0 has just been published and is ready for you to grab. It brings gradual improvements into dataflow as well as a few other domains. Some highlights:</p>
<ul>
<li><code>LazyDataflowVariable</code> added to allow for lazy asynchronous values</li>
<li>Timeout for Selects</li>
<li>Added a Promise-based API for value selection through the Select class</li>
<li>Enabled listening for bind errors on DataflowVariables</li>
<li>Minor API improvement affecting Promise and DataflowReadChannel</li>
<li>Protecting agent&rsquo;s blocking methods from being called from within commands</li>
<li>Updated to the latest 0.7.0 GA version of Multiverse</li>
<li>Migrated to Groovy 2.0</li>
<li>Used <code>@CompileStatic</code> where appropriate</li>
<li>A few bug fixes</li>
</ul>
<p>You can <a href="http://gpars.codehaus.org/Download">download</a> GPars 1.1.0 directly or <a href="http://gpars.codehaus.org/Integration">grab it from the maven repo</a>.</p>
<p>Have a lot of fun trying out GPars 1.1.0!</p></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Glace à la menthe maison</title><link>https://glaforge.dev/posts/2013/07/15/glace-a-la-menthe-maison/</link><pubDate>Mon, 15 Jul 2013 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2013/07/15/glace-a-la-menthe-maison/</guid><description>&lt;p>&lt;figure>
&lt;a href="#img-d13e59f89a4698003411f9423709af15">
&lt;img src="https://glaforge.dev/img/misc/d7a50452ecab11e2a64d22000a9f1590_7.jpg"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-d13e59f89a4698003411f9423709af15">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/d7a50452ecab11e2a64d22000a9f1590_7.jpg"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>C&amp;rsquo;est l&amp;rsquo;été, il fait très chaud, et j&amp;rsquo;ai plein de menthe dans mon jardin&amp;hellip; d&amp;rsquo;où l&amp;rsquo;idée de faire un dessert rafraîchissant : une glace à la menthe maison !&lt;/p>
&lt;p>Un petit problème aussi : je n&amp;rsquo;ai pas de sorbetière, donc il faut faire sans !&lt;/p>
&lt;p>Alors que nous faut-il :&lt;/p>
&lt;ul>
&lt;li>25 cl de lait&lt;/li>
&lt;li>une bonne grosse poignée de feuilles de menthe fraîche&lt;/li>
&lt;li>2 oeufs (2 jaunes et 2 blancs)&lt;/li>
&lt;li>60 g de sucre en poudre, ou idéalement sucre glace (10 g + 50 g)&lt;/li>
&lt;li>25 cl de crème liquide (non allégée)&lt;/li>
&lt;li>1 pincée de sel&lt;/li>
&lt;/ul>
&lt;p>Tout d&amp;rsquo;abord, il faut laisser infuser la menthe dans le lait, dans une casserole, pendant une bonne vingtaine de minutes, à couvert, sans faire bouillir le lait. Cela nous donnerait un bon lait au goût de menthe !&lt;/p></description><content:encoded>
<![CDATA[<p><figure>
  <a href="#img-d13e59f89a4698003411f9423709af15">
    <img src="/img/misc/d7a50452ecab11e2a64d22000a9f1590_7.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-d13e59f89a4698003411f9423709af15">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/d7a50452ecab11e2a64d22000a9f1590_7.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>C&rsquo;est l&rsquo;été, il fait très chaud, et j&rsquo;ai plein de menthe dans mon jardin&hellip; d&rsquo;où l&rsquo;idée de faire un dessert rafraîchissant : une glace à la menthe maison !</p>
<p>Un petit problème aussi : je n&rsquo;ai pas de sorbetière, donc il faut faire sans !</p>
<p>Alors que nous faut-il :</p>
<ul>
<li>25 cl de lait</li>
<li>une bonne grosse poignée de feuilles de menthe fraîche</li>
<li>2 oeufs (2 jaunes et 2 blancs)</li>
<li>60 g de sucre en poudre, ou idéalement sucre glace (10 g + 50 g)</li>
<li>25 cl de crème liquide (non allégée)</li>
<li>1 pincée de sel</li>
</ul>
<p>Tout d&rsquo;abord, il faut laisser infuser la menthe dans le lait, dans une casserole, pendant une bonne vingtaine de minutes, à couvert, sans faire bouillir le lait. Cela nous donnerait un bon lait au goût de menthe !</p>
<p>Par ailleurs, nous devons préparer une chantilly, avec la crème (non allégée, car sinon la chantilly ne montera pas, car c&rsquo;est la matière grasse qui permet à la chantilly de prendre) avec les 25 cl de crème fraîche et 50 g de sucre. Autre petit conseil pour la chantilly, si vous faites ça dans un saladier, un cul de poule, etc, mettez le d&rsquo;abord au frais, ce sera plus facile pour réussir sa chantilly.</p>
<p>Dans un bol, séparer les blancs et les jaunes. Avec les jaunes, mélanger les 10 g de sucre restant, et battre le tout pour que cela blanchisse un peu.</p>
<p>Une fois la menthe bien infusée dans le lait, réservez le lait menthe. Puis lorsqu&rsquo;il a refroidit un peu, vous pourrez le verser et mélanger avec les jaunes d&rsquo;oeufs sucrés.<br />
Mélanger ensuite le lait, les jaunes avec votre crème chantilly.</p>
<p>Enfin, à côté, préparer des blancs en neige, pour donner un peu de volume. Donc avec les blancs, rajouter une pincée de sel, et avec le batteur, montez les blancs en neige jusqu&rsquo;à ce qu&rsquo;ils deviennent bien ferme.</p>
<p>Une fois les blancs prêts, vous devez incorporer délicatement ces blancs en neige dans la préparation précédente (notre mélange jaune, lait et crème chantilly), avec un mouvement circulaire du fond vers le dessus, pour garder le volume et l&rsquo;air des blancs.</p>
<p>Ensuite, vous mettez tout cela à congeler dans votre congélateur pendant quelques heures.</p>
<p>Après quelques heures, si vous êtes patients, vous pourrez ressortir votre glace pour la mixer dans un mixeur pour la rendre plus onctueuse, puis la remettre au congélateur. Si vous voulez, avant de la remettre une seconde fois au congélateur, vous pourrez y rajouter des copeaux de chocolat noir, voire quelques feuilles de menthe fraîche bien hachées très finement (de trop gros bouts de feuille ne seraient pas très agréables sous la langue).</p>
<p>Cette recette est grosso modo pour 3 personnes. Ca nous donne environ 6-8 boules de glace. On peut évidement doubler les proportions si on en veut plus.</p>
<p>Dernière petite chose, vous remarquerez que la glace à la menthe n&rsquo;est pas&hellip; verte, comme on peut l&rsquo;imaginer ! Le vert des glaces ou des sirops est donné par les colorants ! Alors comme votre glace est plutôt beige (grâce à la couleur du jaune d&rsquo;oeuf), si vous en avez, vous pouvez rajouter 3 gouttes de colorant alimentaire bleu, pour obtenir un joli vert !</p>
<p>Et voila, le tour est joué, bonne dégustation !</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy on instantserver.io thanks to GVM</title><link>https://glaforge.dev/posts/2013/06/18/groovy-on-instantserver-io-thanks-to-gvm/</link><pubDate>Tue, 18 Jun 2013 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2013/06/18/groovy-on-instantserver-io-thanks-to-gvm/</guid><description>&lt;p>I recently came across &lt;a href="http://instantserver.io/">InstantServer.io&lt;/a>, a nice little service that allows you to instantiate an Ubuntu server for testing, for 35 minutes, on Amazon EC2. You click on the green button, and you&amp;rsquo;re given an account and a password, as well as the details to connect onto the machine through SSH.&lt;/p>
&lt;p>Please note that the service seems to be victim of its success, as requesting a new server takes more and more time it seems.&lt;/p></description><content:encoded>
<![CDATA[<p>I recently came across <a href="http://instantserver.io/">InstantServer.io</a>, a nice little service that allows you to instantiate an Ubuntu server for testing, for 35 minutes, on Amazon EC2. You click on the green button, and you&rsquo;re given an account and a password, as well as the details to connect onto the machine through SSH.</p>
<p>Please note that the service seems to be victim of its success, as requesting a new server takes more and more time it seems.</p>
<p>I immediately thought about installing and launching Groovy on those remote servers, using <a href="http://gvmtool.net/">GVM</a>. However, there&rsquo;s no &ldquo;unzip&rdquo; command installed, and there&rsquo;s no JDK as well. So there are a few extra steps to be taken. I wanted to blog about the details (which I&rsquo;m actually doing now!), but Tim Yates beat me to it with his detailed instructions!</p>
<p>So my task is now simplified, and I&rsquo;ll simply copy the instructions here, thanks Tim!</p>
<ul>
<li>
<p>Create an instant server at <a href="http://instantserver.io/">http://instantserver.io/</a></p>
</li>
<li>
<p>Log into it using the username/password they supply</p>
</li>
<li>
<p>Run:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo apt-get install unzip
</span></span></code></pre></div></li>
<li>
<p>And when that&rsquo;s done, run:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo apt-get install openjdk-7-jre-headless <span style="color:#007020">export</span> JAVA<span style="color:#4070a0;font-weight:bold">\_</span>HOME<span style="color:#666">=</span>/usr/lib/jvm/java-7-openjdk-amd64
</span></span></code></pre></div></li>
<li>
<p>Then, install GVM:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -s get.gvmtool.net | bash
</span></span></code></pre></div></li>
<li>
<p>And source the GVM script:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#007020">source</span> <span style="color:#4070a0">&#34;/home/ubuntu/.gvm/bin/gvm-init.sh&#34;</span>
</span></span></code></pre></div></li>
<li>
<p>Install Groovy with GVM:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gvm install groovy
</span></span></code></pre></div></li>
<li>
<p>And test:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>groovy -e <span style="color:#4070a0">&#34;println &#39;Hello&#39;&#34;</span>
</span></span></code></pre></div></li>
</ul>
<p>Voilà !</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>My GR8Conf presentation on Groovy</title><link>https://glaforge.dev/talks/2013/05/31/my-gr8conf-presentation-on-groovy/</link><pubDate>Fri, 31 May 2013 00:00:00 +0200</pubDate><guid>https://glaforge.dev/talks/2013/05/31/my-gr8conf-presentation-on-groovy/</guid><description>&lt;p>&lt;a href="http://gr8conf.eu/index">GR8Conf Europe 2013&lt;/a> was such a blast! With close to well over 160 attendees, the conference was a real success, and went flawlessly thanks to the great organization of Søren and his crew. Big congrats to his team! And for the 5th anniversary of the conference, the &lt;a href="http://www.flickr.com/photos/steveonjava/8803004028/">Groovy cakes&lt;/a> were so delicious! It was a pleasure to be back in Copenhagen to meet all my friends from the Groovy community.&lt;/p>
&lt;p>I had a chance to speak about Groovy 2 and beyond, and I&amp;rsquo;m embedding the slides below, if you want to have a look at them. Also, if all goes well, we should even get a video recording of the presentation(s) this year, so fingers crossed!&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://gr8conf.eu/index">GR8Conf Europe 2013</a> was such a blast! With close to well over 160 attendees, the conference was a real success, and went flawlessly thanks to the great organization of Søren and his crew. Big congrats to his team! And for the 5th anniversary of the conference, the <a href="http://www.flickr.com/photos/steveonjava/8803004028/">Groovy cakes</a> were so delicious! It was a pleasure to be back in Copenhagen to meet all my friends from the Groovy community.</p>
<p>I had a chance to speak about Groovy 2 and beyond, and I&rsquo;m embedding the slides below, if you want to have a look at them. Also, if all goes well, we should even get a video recording of the presentation(s) this year, so fingers crossed!</p>
<script async class="speakerdeck-embed" data-id="38ba7100a5be013000c342db8da5a4ed" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Which JDK versions do you use?</title><link>https://glaforge.dev/posts/2013/05/31/which-jdk-versions-do-you-use/</link><pubDate>Fri, 31 May 2013 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2013/05/31/which-jdk-versions-do-you-use/</guid><description>&lt;p>On the &lt;a href="http://groovy.codehaus.org">Groovy&lt;/a> project team, we were wondering about which base JDK to support.&lt;/p>
&lt;p>So far, even the latest and greatest Groovy (version 2.1.4 just released today), we still support JDK 5 — despite being an End-of-Life&amp;rsquo;d product for a long while already.&lt;/p>
&lt;p>At conferences, I often ask the question on which versions of the JDK attendees are using, locally and in production, and it seems that JDK 5 has almost totally vanished off the face of the planet, but despite JDK 7 being quite mature now, it&amp;rsquo;s still JDK 6 that&amp;rsquo;s more widely deployed.&lt;/p></description><content:encoded>
<![CDATA[<p>On the <a href="http://groovy.codehaus.org">Groovy</a> project team, we were wondering about which base JDK to support.</p>
<p>So far, even the latest and greatest Groovy (version 2.1.4 just released today), we still support JDK 5 — despite being an End-of-Life&rsquo;d product for a long while already.</p>
<p>At conferences, I often ask the question on which versions of the JDK attendees are using, locally and in production, and it seems that JDK 5 has almost totally vanished off the face of the planet, but despite JDK 7 being quite mature now, it&rsquo;s still JDK 6 that&rsquo;s more widely deployed.</p>
<p>To know more about the situation, my Groovy colleague <a href="http://www.jroller.com/melix/">Cédric</a> crafted a little Google form to run a <a href="https://docs.google.com/forms/d/1KYMG09CFBwn4m4xN2VfGuhWw2VPs39S87IRbIfPwRWw/viewform?pli=1&amp;edit_requested=true">survey</a> among our users, with three key questions:</p>
<blockquote>
<ol>
<li>What version of Java do you use in production?</li>
<li>What version of Java do you use for development?</li>
<li>What version of Java do you plan to use in the next 12 months?</li>
</ol></blockquote>
<p>You can view the <a href="https://docs.google.com/forms/d/1KYMG09CFBwn4m4xN2VfGuhWw2VPs39S87IRbIfPwRWw/viewanalytics">detailed results</a> (and you can actually still vote), but to sum it up, luckily, there&rsquo;s nobody still using Java 1.4, which is good news, and only a handful of people still stuck with JDK 5 (4% in production). Thus Java 6 and 7 are safe bets as a base for your projects, with roughly 57% of Java 6 and and 39% of Java 7 in production, this Java 6 still winning by almost 50% over 7! And in development, developers are more eager to using the latest JDK, with twice as much Java 7 than Java 6.</p>
<p><a href="https://docs.google.com/forms/d/1KYMG09CFBwn4m4xN2VfGuhWw2VPs39S87IRbIfPwRWw/viewanalytics"><figure>
  <a href="#img-4b14cea5aa71f9a60871916cf95b416e">
    <img src="/img/misc/jdk-versions-adoption.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-4b14cea5aa71f9a60871916cf95b416e">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/jdk-versions-adoption.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</a></p>
<p>As for the Groovy project, we&rsquo;re thinking of mandating Java 7 (essentially because of &ldquo;invoke dynamic&rdquo;) as the base Java version for Groovy 3, and as this release is slated for late Q1 next year, the adoption of Java 7 should have increased significantly in production — and anyway, we&rsquo;ll still support Groovy 2.X in parallel for &ldquo;older&rdquo; JDKs. But the question was more for the upcoming Groovy 2.2, which should be released in a couple of months or so, if we should go with Java 6 as the base requirement. We haven&rsquo;t yet made our decision, but considering 96% of respondents are using Java 6 or newer, that&rsquo;s not a risky bet to make that decision!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Gaelyk 2.0 is released</title><link>https://glaforge.dev/posts/2013/05/16/gaelyk-2-0-is-released/</link><pubDate>Thu, 16 May 2013 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2013/05/16/gaelyk-2-0-is-released/</guid><description>&lt;p>This is with great pleasure that the Gaelyk team announces the &lt;strong>release of Gaelyk 2.0&lt;/strong>, the lightweight toolkit for developing applications on Google App Engine with the Groovy dynamic language.&lt;/p>
&lt;p>For the impatient, go directly to the &lt;a href="http://gaelyk.appspot.com/download">download page&lt;/a>!&lt;/p>
&lt;p>It&amp;rsquo;s been long in the making, and there are quite a lot of new features to speak about, like the &lt;strong>migration to Groovy 2.1 and GAE 1.8&lt;/strong>, the new &lt;strong>Search DSL&lt;/strong>, &lt;strong>improvements to the URL routing system&lt;/strong>, various &lt;strong>performance optimizations&lt;/strong>, and more.&lt;/p></description><content:encoded>
<![CDATA[<p>This is with great pleasure that the Gaelyk team announces the <strong>release of Gaelyk 2.0</strong>, the lightweight toolkit for developing applications on Google App Engine with the Groovy dynamic language.</p>
<p>For the impatient, go directly to the <a href="http://gaelyk.appspot.com/download">download page</a>!</p>
<p>It&rsquo;s been long in the making, and there are quite a lot of new features to speak about, like the <strong>migration to Groovy 2.1 and GAE 1.8</strong>, the new <strong>Search DSL</strong>, <strong>improvements to the URL routing system</strong>, various <strong>performance optimizations</strong>, and more.</p>
<p>Please be sure to have a look at the list of <a href="http://gaelyk.appspot.com/download">changes on the release notes section of the download page</a>. In particular, be sure to have a look at the <strong>breaking changes</strong> which might affect your application when upgrading.</p>
<p>We&rsquo;re very happy to have Gaelyk 2.0 now officially available in your hands, and we&rsquo;d like to thank you for your support, for your contributions, and for such a friendly community!</p>
<p>Enjoy!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Creating a new project with Gradle</title><link>https://glaforge.dev/posts/2013/04/17/creating-a-new-project-with-gradle/</link><pubDate>Wed, 17 Apr 2013 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2013/04/17/creating-a-new-project-with-gradle/</guid><description>&lt;p>Since I started using &lt;a href="http://www.gradle.org/">Gradle&lt;/a> for building my projects, as soon as I needed to create a brand new project from scratch, I wished that Gradle guided my hand and created the right directory structure and minimal Gradle build script.&lt;/p>
&lt;p>A bit less than two years ago, I was inspired by Dierk&amp;rsquo;s &lt;a href="https://gist.github.com/Dierk/1271516">gist&lt;/a> providing a basic build that provided two tasks, one for creating the directory layout, and the other one to create the Gradle wrapper. I turned that gist into a &lt;a href="https://glaforge.dev/posts/2011/11/25/handy-gradle-startup-script/">blog post of my own&lt;/a>, with just minor version updates, as I wanted to remember how to do that the next time I&amp;rsquo;d create a new Gradle-based project.&lt;/p></description><content:encoded>
<![CDATA[<p>Since I started using <a href="http://www.gradle.org/">Gradle</a> for building my projects, as soon as I needed to create a brand new project from scratch, I wished that Gradle guided my hand and created the right directory structure and minimal Gradle build script.</p>
<p>A bit less than two years ago, I was inspired by Dierk&rsquo;s <a href="https://gist.github.com/Dierk/1271516">gist</a> providing a basic build that provided two tasks, one for creating the directory layout, and the other one to create the Gradle wrapper. I turned that gist into a <a href="https://glaforge.dev/posts/2011/11/25/handy-gradle-startup-script/">blog post of my own</a>, with just minor version updates, as I wanted to remember how to do that the next time I&rsquo;d create a new Gradle-based project.</p>
<p>But we were not the only ones missing this capability in Gradle!</p>
<p>More recently, <a href="http://blog.proxerd.pl/article/my-gradle-init-script">Marcin leveraged the init script capabilities of Gradle</a> to make the process a bit smoother, so that we don&rsquo;t have to blindly and stupidly copy and paste the same sample build script each time. With his approach, we add the project layout and wrapper creation in the ~/.gradle/init.gradle script, so that it&rsquo;s available in your new projects by default.</p>
<p>Last but not least, just the other day, Erik came up with a <a href="http://www.jworks.nl/2013/04/16/creating-projects-with-gradle/">similar approach</a> as the original gist for creating projects, with some more flags to customize which directories we want created.</p>
<p>As it seems we&rsquo;re all reinventing the wheel, I wondered on twitter <a href="https://twitter.com/glaforge/status/324484467911704576">how come this basic functionality wasn&rsquo;t already included in Gradle</a>! And in matter of minutes, I got some additional pointers about other archetypes and template solutions, like the <a href="https://github.com/svene/dt_java">dt_java</a> project which provides templates for Gradle-based projects amon other things, the <a href="https://github.com/townsfolk/gradle-templates">Gradle templates plugin</a>.</p>
<p>But the good news is that this often needed feature is actually getting some attention from our Gradle(ware) friends, and we&rsquo;re going to see some form of project creation in Gradle 1.6 soon, as shown in this <a href="https://github.com/gradle/gradle/blob/master/design-docs/build-initialisation.md">design document</a>.
Ultimately, creating a template project and a Gradle wrapper will likely be as simple as:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gradle setupBuild --type groovy-library
</span></span><span style="display:flex;"><span>gradle wrapper
</span></span></code></pre></div><p>And then, no more hacks needed, you&rsquo;ll have a nice and ready-to-build project in matter of mere seconds, without resorting to our various tactics listed above!
I&rsquo;m looking forward to Gradle 1.6 even if just for that new capabilities we&rsquo;ve all been longing for!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Contribute a dedicated CI server for Groovy</title><link>https://glaforge.dev/posts/2013/04/16/contribute-a-dedicated-ci-server-for-groovy/</link><pubDate>Tue, 16 Apr 2013 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2013/04/16/contribute-a-dedicated-ci-server-for-groovy/</guid><description>&lt;p>The Groovy team is always looking at ways to further improve and encourage the collaboration and contributions from the community.&lt;/p>
&lt;p>Today, the we are** looking for a sponsor for a dedicated server** that would host our &lt;strong>new Continuous Integration server&lt;/strong>. If you or your company is interested in helping Groovy by contributing hardware and/or hosting, please contact us.&lt;/p>
&lt;p>The details of the project are available &lt;a href="http://docs.codehaus.org/display/GROOVY/A+new+CI+server+for+the+Groovy+project">here&lt;/a>.&lt;/p>
&lt;p>On behalf of the Groovy team, thanks in advance for your help and support!&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy team is always looking at ways to further improve and encourage the collaboration and contributions from the community.</p>
<p>Today, the we are** looking for a sponsor for a dedicated server** that would host our <strong>new Continuous Integration server</strong>. If you or your company is interested in helping Groovy by contributing hardware and/or hosting, please contact us.</p>
<p>The details of the project are available <a href="http://docs.codehaus.org/display/GROOVY/A+new+CI+server+for+the+Groovy+project">here</a>.</p>
<p>On behalf of the Groovy team, thanks in advance for your help and support!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Joins us for GR8Conf Europe! Early bird price ending soon.</title><link>https://glaforge.dev/posts/2013/04/03/joins-us-for-gr8conf-europe-early-bird-ending-soon/</link><pubDate>Wed, 03 Apr 2013 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2013/04/03/joins-us-for-gr8conf-europe-early-bird-ending-soon/</guid><description>&lt;p>Like every year, &lt;a href="http://gr8conf.eu/">GR8Conf Europe&lt;/a> takes place in the beautiful city of Copenhagen in Denmark. It&amp;rsquo;s where all the talented and cool Groovy ecosystem folks meet, and spread the love about their technologies, including Groovy, Grails, Gradle, Spock, GPars, and more.&lt;/p>
&lt;p>I&amp;rsquo;ll be speaking about the latest developments on the Groovy 2.x branch, and what our plans are going forward. You can also have a look at the preliminary &lt;a href="http://gr8conf.eu/Agenda">agenda&lt;/a> published on the GR8Conf website, to see familiar &lt;a href="http://gr8conf.eu/Speakers">names&lt;/a> like Jürgen Höller, Venkat Subramaniam, Dierk König, Graeme Rocher, Burt Beckwith, Mr Haki, Luke Daley, Russel Winder&amp;hellip; You&amp;rsquo;re sure to find some great content delivered by the experts themselves!&lt;/p></description><content:encoded>
<![CDATA[<p>Like every year, <a href="http://gr8conf.eu/">GR8Conf Europe</a> takes place in the beautiful city of Copenhagen in Denmark. It&rsquo;s where all the talented and cool Groovy ecosystem folks meet, and spread the love about their technologies, including Groovy, Grails, Gradle, Spock, GPars, and more.</p>
<p>I&rsquo;ll be speaking about the latest developments on the Groovy 2.x branch, and what our plans are going forward. You can also have a look at the preliminary <a href="http://gr8conf.eu/Agenda">agenda</a> published on the GR8Conf website, to see familiar <a href="http://gr8conf.eu/Speakers">names</a> like Jürgen Höller, Venkat Subramaniam, Dierk König, Graeme Rocher, Burt Beckwith, Mr Haki, Luke Daley, Russel Winder&hellip; You&rsquo;re sure to find some great content delivered by the experts themselves!</p>
<p>I&rsquo;d also like to highlight that you should hurry up to <a href="http://gr8conf.eu/Registration">register</a> if you want to benefit from the Early Bird registration price! This agressive price last for a couple more days, so be fast!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.1 is released</title><link>https://glaforge.dev/posts/2013/01/25/groovy-2-1-is-released/</link><pubDate>Fri, 25 Jan 2013 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2013/01/25/groovy-2-1-is-released/</guid><description>&lt;p>Here&amp;rsquo;s my announcement for the Groovy 2.1 release, and be sure to check out the &lt;a href="http://bit.ly/groovy21notes">full release notes of the release&lt;/a>!&lt;/p>
&lt;blockquote>
&lt;p>The Groovy team is pleased to announce the &lt;strong>release of Groovy 2.1.0&lt;/strong>.&lt;br />
With over &lt;strong>1.7 million downloads in 2012&lt;/strong>, a &lt;strong>strong ecosystem&lt;/strong>, Groovy continues its development and adoption, refines existing features and evolves new ones.&lt;/p>
&lt;p>In this new release, Groovy 2.1:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>offers full support for the JDK 7 “invoke dynamic”&lt;/strong> bytecode instruction and API,&lt;/p></description><content:encoded>
<![CDATA[<p>Here&rsquo;s my announcement for the Groovy 2.1 release, and be sure to check out the <a href="http://bit.ly/groovy21notes">full release notes of the release</a>!</p>
<blockquote>
<p>The Groovy team is pleased to announce the <strong>release of Groovy 2.1.0</strong>.<br />
With over <strong>1.7 million downloads in 2012</strong>, a <strong>strong ecosystem</strong>, Groovy continues its development and adoption, refines existing features and evolves new ones.</p>
<p>In this new release, Groovy 2.1:</p>
<ul>
<li>
<p><strong>offers full support for the JDK 7 “invoke dynamic”</strong> bytecode instruction and API,</p>
</li>
<li>
<p>goes beyond conventional static type checking capabilities with a <strong>special annotation to assist with documentation and type safety of DSLs</strong> and adds <strong>static type checker extensions</strong>,</p>
</li>
<li>
<p>provides <strong>additional compilation customization</strong> options,</p>
</li>
<li>
<p>features a <strong>meta-annotation facility</strong> for combining annotations elegantly,</p>
</li>
<li>
<p>and provides various other enhancements and minor improvements.</p>
</li>
</ul>
<p>Please read all the details about the <strong>new features and improvements</strong> in the <strong>Groovy 2.1 release notes</strong> document:<br />
<a href="http://bit.ly/groovy21notes">http://bit.ly/groovy21notes</a></p>
<p>You can <strong>download Groovy 2.1.0</strong> from the Download area:<br />
<a href="http://groovy.codehaus.org/Download?nc">http://groovy.codehaus.org/Download?nc</a></p>
<p>And have a look at the <strong>JIRA tickets</strong> we worked on:<br />
<a href="http://bit.ly/groovy21jira">http://bit.ly/groovy21jira</a></p>
<p>Thanks a lot to all our users, contributors and committers who made this release possible.</p>
<p>I&rsquo;d particularly like thank our <strong>new committers</strong>: Pascal Schumacher, André Steingreß and Tim Yates, for all their feedback and various contributions. Welcome to the team!</p>
<p>The Groovy team is looking forward to your feedback on this new release!</p>
<p>Keep on groovy&rsquo;ing!</p></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Brownie dans un mug</title><link>https://glaforge.dev/posts/2013/01/22/brownie-dans-un-mug/</link><pubDate>Tue, 22 Jan 2013 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2013/01/22/brownie-dans-un-mug/</guid><description>&lt;p>L&amp;rsquo;autre jour sur Facebook, je suis tombé sur cette &lt;a href="http://twistedsifter.com/2013/01/50-life-hacks-to-simplify-your-world/">liste de &amp;ldquo;life-hacks&amp;rdquo;&lt;/a>, dont certains sont vraiment malins&amp;hellip; et parmi eux, il y avait une recette de brownie dans un mug. Je l&amp;rsquo;ai retrouvée &lt;a href="http://blog.gopromos.com/how-to-make-brownie-in-a-mug/">là&lt;/a>, mais j&amp;rsquo;ai également trouvé d&amp;rsquo;autres variantes, comme &lt;a href="http://a-cuisiner.lindt.fr/recettes-a-cuisiner/desserts-gourmands/brownie-fondant-coulant-dans-un-mug/imprimer">celle de Lindt&lt;/a>.&lt;/p>
&lt;p>Du coup, par gourmandise et pour faire plaisir à ma fille, nous nous sommes concocté notre propre version de ces deux recettes, que je vous livre aujourd&amp;rsquo;hui !&lt;/p>
&lt;p>Déjà, les ingrédients :&lt;/p></description><content:encoded>
<![CDATA[<p>L&rsquo;autre jour sur Facebook, je suis tombé sur cette <a href="http://twistedsifter.com/2013/01/50-life-hacks-to-simplify-your-world/">liste de &ldquo;life-hacks&rdquo;</a>, dont certains sont vraiment malins&hellip; et parmi eux, il y avait une recette de brownie dans un mug. Je l&rsquo;ai retrouvée <a href="http://blog.gopromos.com/how-to-make-brownie-in-a-mug/">là</a>, mais j&rsquo;ai également trouvé d&rsquo;autres variantes, comme <a href="http://a-cuisiner.lindt.fr/recettes-a-cuisiner/desserts-gourmands/brownie-fondant-coulant-dans-un-mug/imprimer">celle de Lindt</a>.</p>
<p>Du coup, par gourmandise et pour faire plaisir à ma fille, nous nous sommes concocté notre propre version de ces deux recettes, que je vous livre aujourd&rsquo;hui !</p>
<p>Déjà, les ingrédients :</p>
<ul>
<li>8 carrés de chocolat dessert (1/4 ou 1/5 de tablette)</li>
<li>1 cuillère de beurre</li>
<li>3 cuillères à soupe d&rsquo;eau</li>
<li>4 cuillères à soupe de farine</li>
<li>3 cuillères à soupe de sucre</li>
<li>1 pincée de sel</li>
<li>2 noix</li>
</ul>
<p>D&rsquo;abord, mettre le chocolat, le beurre et l&rsquo;eau ensemble au fond d&rsquo;un mug. Faire fondre le tout pendant environ 30 secondes dans le micro-onde. Après cela, rajouter la farine, le sucre et le sel, et bien mélanger le tout pour faire un mélange homogène. Le petit plus, deux noix pour rajouter un peu de croquant ! Enfin, remettre le tout dans le micro-onde pour une bonne minute, et le tour est joué.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>First beta of Groovy 2.1 released</title><link>https://glaforge.dev/posts/2012/12/21/first-beta-of-groovy-2-1-released/</link><pubDate>Fri, 21 Dec 2012 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2012/12/21/first-beta-of-groovy-2-1-released/</guid><description>&lt;p>Since the world didn&amp;rsquo;t seem to end today, it is with great pleasure that we bring under your Christmas tree the following presents: the &lt;strong>release of Groovy 2.1.0-beta-1 and Groovy 2.0.6&lt;/strong>.&lt;br />
Groovy 2.0.6 is a bug fix release for our Groovy 2.0.X line, whereas Groovy 2.1.0-beta-1 contains new features.&lt;br />
You can download Groovy at the usual location: &lt;a href="http://groovy.codehaus.org/Download">http://groovy.codehaus.org/Download&lt;/a>&lt;/p>
&lt;p>The JIRA release notes can be found here:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Groovy 2.1.0-beta-1: &lt;a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;version=18598">http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;version=18598&lt;/a>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Groovy 2.0.6: &lt;a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;version=18852">http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;version=18852&lt;/a>&lt;/p></description><content:encoded>
<![CDATA[<p>Since the world didn&rsquo;t seem to end today, it is with great pleasure that we bring under your Christmas tree the following presents: the <strong>release of Groovy 2.1.0-beta-1 and Groovy 2.0.6</strong>.<br />
Groovy 2.0.6 is a bug fix release for our Groovy 2.0.X line, whereas Groovy 2.1.0-beta-1 contains new features.<br />
You can download Groovy at the usual location: <a href="http://groovy.codehaus.org/Download">http://groovy.codehaus.org/Download</a></p>
<p>The JIRA release notes can be found here:</p>
<ul>
<li>
<p>Groovy 2.1.0-beta-1: <a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=18598">http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=18598</a></p>
</li>
<li>
<p>Groovy 2.0.6: <a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=18852">http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=18852</a></p>
</li>
</ul>
<p>More extensive release notes will be available for the final Groovy 2.1.0 release, but please let me highlight the following key new features:</p>
<ul>
<li><strong>complete invoke dynamic support</strong> when running with the &ldquo;indy&rdquo; JAR on JDK 7</li>
<li><strong>upgrade to GPars 1.0</strong>: the Groovy distribution now bundles the GPars 1.0 final release</li>
<li><strong><code>@DelegatesTo</code> annotation</strong>: to help IDEs and the static type checker and compiler to know that method calls in a method parameter closure are delegated to another parameter of the method &ndash; nice for DSLs like in Gradle build files</li>
<li><strong>custom type checking extensions</strong>: so you can type check your DSLs at compile-time with your own logic</li>
<li>a <strong>meta-annotation</strong> system: which allows you to define a new annotation actually combining several others &ndash; which also means being able to apply several AST transformations with a single custom annotation</li>
<li><strong>custom base script class flag</strong> for the groovyc compiler: to set a base script class when compiling Groovy scripts</li>
<li><strong>compiler configuration script</strong>: to let you define various configuration options for the Groovy compiler, like specifying custom file extensions, various compilation customizers to apply, etc.</li>
<li><strong>compilation customizer builder</strong>: a special builder for specifying compilation customizers</li>
<li><code>jar://</code>, <code>file://</code>, <code>http://</code> prefix support for launching Groovy scripts from the command line</li>
</ul>
<p>Although more details will be provided later on for the final release, please don&rsquo;t hesitate to ask questions about those new features and improvements till then.</p>
<p>A little reminder on the roadmap, we intend on releasing a release candidate of Groovy 2.1 the second week of January, and if all goes well (if we don&rsquo;t need another RC for some last minute issues), we&rsquo;ll go straight to the final release at the end of January.</p>
<p>Thanks a lot for your feedback, your bug reports, your contributions, your pull requests! It&rsquo;s with such a great community of users that we make Groovy a joy to use in our every day projects.</p>
<p>The whole development team joins me to wish you the best for the holidays and for next year! A Groovy Christmas and a Happy New Year to all!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>GPars reaches 1.0!</title><link>https://glaforge.dev/posts/2012/12/19/gpars-reaches-1-0/</link><pubDate>Wed, 19 Dec 2012 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2012/12/19/gpars-reaches-1-0/</guid><description>&lt;p>Václav Pech just &lt;a href="http://www.jroller.com/vaclav/entry/i_like_the_smell_of">announced the final release of GPars 1.0&lt;/a>, the concurrency and parallel toolkit for Groovy and Java.&lt;/p>
&lt;p>We&amp;rsquo;ll bundle &lt;a href="http://gpars.codehaus.org/">GPars 1.0&lt;/a> in the Groovy 2.1 distribution coming very soon!&lt;/p>
&lt;p>Let me quote some of the key changes and enhancements of this release (quoting from the &amp;ldquo;what&amp;rsquo;s new&amp;rdquo; section of the &lt;a href="http://www.gpars.org/1.0.0/guide/guide/gettingStarted.html#gettingStarted_what'sNew">user guide&lt;/a>):&lt;/p>
&lt;blockquote>
&lt;p>Asynchronous functions&lt;/p>
&lt;ul>
&lt;li>Allowed for delayed and explicit thread pool assignment strategies for asynchronous functions&lt;/li>
&lt;li>Performance tuning to the asynchronous closure invocation mechanism&lt;/li>
&lt;/ul>
&lt;p>Parallel collections&lt;/p></description><content:encoded>
<![CDATA[<p>Václav Pech just <a href="http://www.jroller.com/vaclav/entry/i_like_the_smell_of">announced the final release of GPars 1.0</a>, the concurrency and parallel toolkit for Groovy and Java.</p>
<p>We&rsquo;ll bundle <a href="http://gpars.codehaus.org/">GPars 1.0</a> in the Groovy 2.1 distribution coming very soon!</p>
<p>Let me quote some of the key changes and enhancements of this release (quoting from the &ldquo;what&rsquo;s new&rdquo; section of the <a href="http://www.gpars.org/1.0.0/guide/guide/gettingStarted.html#gettingStarted_what'sNew">user guide</a>):</p>
<blockquote>
<p>Asynchronous functions</p>
<ul>
<li>Allowed for delayed and explicit thread pool assignment strategies for asynchronous functions</li>
<li>Performance tuning to the asynchronous closure invocation mechanism</li>
</ul>
<p>Parallel collections</p>
<ul>
<li>Added a couple of new parallel collection processing methods to keep up with the innovation pace in Groovy</li>
<li>Merged the extra166y library into GPars</li>
</ul>
<p>Actors</p>
<ul>
<li>StaticDispatchActor has been added to provide easier to create and better performing alternative to <em>DynamicDispatchActor</em></li>
<li>A new method <em>sendAndPromise</em> has been added to actors to send a message and get a promise for the future actor&rsquo;s reply</li>
</ul>
<p>Dataflow</p>
<ul>
<li>Operator and selector speed-up</li>
<li>Kanban-style dataflow operator management has been added</li>
<li>Chaining of Promises using the new <em>then()</em> method</li>
<li>Exception propagation and handling for Promises</li>
<li>Added a DSL for easy operator pipe-lining</li>
<li>Lifecycle events for operators and selectors were added</li>
<li>Added support for custom error handlers</li>
<li>A generic way to shutdown dataflow networks</li>
<li>An shutdown poison pill with immediate or delayed effect was added</li>
<li>Polished the way operators can be stopped</li>
<li>Added synchronous dataflow variables and channels</li>
<li>Read channels can report their length</li>
</ul></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.0 and beyond at Groovy Grails eXchange</title><link>https://glaforge.dev/talks/2012/12/19/groovy-2-0-and-beyond-at-groovy-grails-exchange/</link><pubDate>Wed, 19 Dec 2012 00:00:00 +0100</pubDate><guid>https://glaforge.dev/talks/2012/12/19/groovy-2-0-and-beyond-at-groovy-grails-exchange/</guid><description>&lt;p>Last week, I had the pleasure to attend and keynote at the 5th edition of the &lt;a href="http://skillsmatter.com/event/groovy-grails/groovy-grails-exchange-2012">Groovy / Grails eXchange conference&lt;/a>, in London. I presented a talk around the &lt;a href="http://skillsmatter.com/podcast/groovy-grails/keynote-speech">new features of Groovy 2.0 and the upcoming 2.1 release&lt;/a>, as well as a few words about what&amp;rsquo;s coming up next.&lt;/p>
&lt;p>You can watch the video online, as &lt;a href="http://skillsmatter.com/">SkillsMatter&lt;/a>, the organizer of the conference, have put all the presentations online in record time!&lt;/p>
&lt;p>And you can have a look at the slides hosted on Speakerdeck below:&lt;/p></description><content:encoded>
<![CDATA[<p>Last week, I had the pleasure to attend and keynote at the 5th edition of the <a href="http://skillsmatter.com/event/groovy-grails/groovy-grails-exchange-2012">Groovy / Grails eXchange conference</a>, in London. I presented a talk around the <a href="http://skillsmatter.com/podcast/groovy-grails/keynote-speech">new features of Groovy 2.0 and the upcoming 2.1 release</a>, as well as a few words about what&rsquo;s coming up next.</p>
<p>You can watch the video online, as <a href="http://skillsmatter.com/">SkillsMatter</a>, the organizer of the conference, have put all the presentations online in record time!</p>
<p>And you can have a look at the slides hosted on Speakerdeck below:</p>
<script async class="speakerdeck-embed" data-id="bcf46160275f0130d81c12313d1a713a" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>InfoQ's readers JVM languages research</title><link>https://glaforge.dev/posts/2012/11/28/infoq-s-readers-jvm-languages-research/</link><pubDate>Wed, 28 Nov 2012 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2012/11/28/infoq-s-readers-jvm-languages-research/</guid><description>&lt;p>&lt;a href="http://www.infoq.com">InfoQ&lt;/a> is running a reader-driven survey titled &amp;ldquo;&lt;a href="http://www.infoq.com/research/next-jvm-language">What&amp;rsquo;s your next JVM language&lt;/a>&amp;rdquo;.&lt;/p>
&lt;p>Among the candidates to be ranked, you&amp;rsquo;ll find:&lt;/p>
&lt;ul>
&lt;li>Groovy&lt;/li>
&lt;li>Java 8&lt;/li>
&lt;li>Ceylon&lt;/li>
&lt;li>Kotlin&lt;/li>
&lt;li>Clojure&lt;/li>
&lt;li>Scala&lt;/li>
&lt;li>Xtend&lt;/li>
&lt;li>Fantom&lt;/li>
&lt;li>Mirah&lt;/li>
&lt;/ul>
&lt;p>As well as the languages ported to the JVM like:&lt;/p>
&lt;ul>
&lt;li>JRuby&lt;/li>
&lt;li>Jython&lt;/li>
&lt;li>Javascript (Rhino and soon Nashorn)&lt;/li>
&lt;li>Erlang&lt;/li>
&lt;li>Scheme&lt;/li>
&lt;/ul>
&lt;p>The interesting aspect of the survey is that it&amp;rsquo;s not just a mere poll, but you can rank those JVM languages across two criterion: the percentage of lines of code (less or more), and the adoption (like now, in 3 months, in a year). It certainly gives more perspective than a mere poll on your favorite language. The downside might be that it&amp;rsquo;s too easy to rank the ones you dislike negatively without objectivity, but well, polls are like that too :-)&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://www.infoq.com">InfoQ</a> is running a reader-driven survey titled &ldquo;<a href="http://www.infoq.com/research/next-jvm-language">What&rsquo;s your next JVM language</a>&rdquo;.</p>
<p>Among the candidates to be ranked, you&rsquo;ll find:</p>
<ul>
<li>Groovy</li>
<li>Java 8</li>
<li>Ceylon</li>
<li>Kotlin</li>
<li>Clojure</li>
<li>Scala</li>
<li>Xtend</li>
<li>Fantom</li>
<li>Mirah</li>
</ul>
<p>As well as the languages ported to the JVM like:</p>
<ul>
<li>JRuby</li>
<li>Jython</li>
<li>Javascript (Rhino and soon Nashorn)</li>
<li>Erlang</li>
<li>Scheme</li>
</ul>
<p>The interesting aspect of the survey is that it&rsquo;s not just a mere poll, but you can rank those JVM languages across two criterion: the percentage of lines of code (less or more), and the adoption (like now, in 3 months, in a year). It certainly gives more perspective than a mere poll on your favorite language. The downside might be that it&rsquo;s too easy to rank the ones you dislike negatively without objectivity, but well, polls are like that too :-)</p>
<p>If you&rsquo;re a Groovy fan like me, be sure to put Groovy in the lower left hand corner ;-)</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.0 online webinar</title><link>https://glaforge.dev/posts/2012/10/22/groovy-2-0-online-webinar/</link><pubDate>Mon, 22 Oct 2012 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/10/22/groovy-2-0-online-webinar/</guid><description>&lt;p>On Thursday, at 15:30 GMT+1, I&amp;rsquo;ll have the pleasure to present a Groovy 2.0 update presentation live, online, in the form of a webinar. The webinar will be showcased through &lt;a href="http://bit.ly/groovy2webinar">webex here&lt;/a>. The video will later be made available on &lt;a href="http://www.youtube.com/user/SpringSourceDev">SpringSource&amp;rsquo;s YouTube channel&lt;/a>, for those who want to see it afterwards at a more convenient time.&lt;/p>
&lt;p>In the meantime, here&amp;rsquo;s the title and abstract of that &lt;a href="http://bit.ly/groovy2webinar">presentation&lt;/a>:&lt;/p>
&lt;blockquote>
&lt;p>What&amp;rsquo;s new in Groovy 2.0?&lt;/p>
&lt;p>Groovy is a very popular alternative language for the JVM that continues to bring more developer productivity and more performance after each release. In this webinar, we&amp;rsquo;ll dive through the new developments in the recently released Groovy 2.0.&lt;/p></description><content:encoded>
<![CDATA[<p>On Thursday, at 15:30 GMT+1, I&rsquo;ll have the pleasure to present a Groovy 2.0 update presentation live, online, in the form of a webinar. The webinar will be showcased through <a href="http://bit.ly/groovy2webinar">webex here</a>. The video will later be made available on <a href="http://www.youtube.com/user/SpringSourceDev">SpringSource&rsquo;s YouTube channel</a>, for those who want to see it afterwards at a more convenient time.</p>
<p>In the meantime, here&rsquo;s the title and abstract of that <a href="http://bit.ly/groovy2webinar">presentation</a>:</p>
<blockquote>
<p>What&rsquo;s new in Groovy 2.0?</p>
<p>Groovy is a very popular alternative language for the JVM that continues to bring more developer productivity and more performance after each release. In this webinar, we&rsquo;ll dive through the new developments in the recently released Groovy 2.0.</p>
<p>A lot of the Groovy users are using the language as a Java scripting language, or sometimes as a better Java. First of all, not all of them need the dynamic capabilities, nor do they need all the library improvements provided by Groovy. For the latter, Groovy becomes more modular with smaller core modules that you can compose. For the former, in addition to its usual dynamic features, Groovy 2.0 adds static type checking, allowing you to ensure the correctness of your code before it ships and quickly spot early errors at compile time. Also, not everybody needs dynamic features at all times, and the natural extension to static type checking is static compilation. With static compilation, Groovy 2.0 generates fast and performant bytecode like Java, and shields key parts of your code base from possible interferences of dynamic features, making critical paths both faster and immune to monkey patching. But when you need fast and performant dynamic logic, Groovy 2.0 also features the integration of the &ldquo;invoke dynamic&rdquo; support coming up with JDK 7 and beyond.</p></blockquote>
<p><strong>Update:</strong> You can look at the <a href="http://fr.slideshare.net/glaforge/groovy-20-webinar">slides on Slideshare</a> and <a href="http://www.youtube.com/watch?v=MwnjnOv9jVE&amp;list=UU7yfnfvEUlXUIfm8rGLwZdA&amp;index=1&amp;feature=plpp_video">watch the video on YouTube</a> on SpringSource&rsquo;s dev channel</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>SpringOne2GX Groovy presentations aftermath</title><link>https://glaforge.dev/talks/2012/10/22/springone2gx-groovy-presentations-aftermath/</link><pubDate>Mon, 22 Oct 2012 00:00:00 +0200</pubDate><guid>https://glaforge.dev/talks/2012/10/22/springone2gx-groovy-presentations-aftermath/</guid><description>&lt;p>Last week, I was in Washington DC for the &lt;a href="http://www.springone2gx.com/conference/washington/2012/10/2gx/event_schedule">SpringOne2GX conference&lt;/a>.&lt;/p>
&lt;p>I&amp;rsquo;ve had the pleasure of presenting twice: first time on an update on Groovy 2.0 (and a recap on key features of Groovy 1.8), and a second time with my friends and colleagues Paul King (Groovy core committer) and Andrew Eisenberg (Groovy/Grails Eclipse tooling lead) about Domain-Specific Languages in Groovy.&lt;/p>
&lt;p>Please see the slides published below:&lt;/p>
&lt;iframe
style="width: 100%; aspect-ratio: 1.62" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"
src="http://www.slideshare.net/slideshow/embed_code/14753365?rel=0" allowfullscreen webkitallowfullscreen mozallowfullscreen> &lt;/iframe>
&lt;br>&lt;br>
&lt;iframe
style="width: 100%; aspect-ratio: 1.62" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"
src="http://www.slideshare.net/slideshow/embed_code/14759852?rel=0" allowfullscreen webkitallowfullscreen mozallowfullscreen> &lt;/iframe>
&lt;br>&lt;br></description><content:encoded>
<![CDATA[<p>Last week, I was in Washington DC for the <a href="http://www.springone2gx.com/conference/washington/2012/10/2gx/event_schedule">SpringOne2GX conference</a>.</p>
<p>I&rsquo;ve had the pleasure of presenting twice: first time on an update on Groovy 2.0 (and a recap on key features of Groovy 1.8), and a second time with my friends and colleagues Paul King (Groovy core committer) and Andrew Eisenberg (Groovy/Grails Eclipse tooling lead) about Domain-Specific Languages in Groovy.</p>
<p>Please see the slides published below:</p>
<iframe
  style="width: 100%; aspect-ratio: 1.62" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"
  src="http://www.slideshare.net/slideshow/embed_code/14753365?rel=0" allowfullscreen webkitallowfullscreen mozallowfullscreen> </iframe>
<br><br>
<iframe
  style="width: 100%; aspect-ratio: 1.62" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"
  src="http://www.slideshare.net/slideshow/embed_code/14759852?rel=0" allowfullscreen webkitallowfullscreen mozallowfullscreen> </iframe>
<br><br>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy sessions at JavaOne 2012</title><link>https://glaforge.dev/posts/2012/09/29/groovy-sessions-at-javaone-2012/</link><pubDate>Sat, 29 Sep 2012 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/09/29/groovy-sessions-at-javaone-2012/</guid><description>&lt;p>Monday I&amp;rsquo;ll be flying to San Francisco for the JavaOne conference, where there will be &lt;a href="https://oracleus.activeevents.com/connect/search.ww?event=javaone#loadSearch-event=javaone&amp;amp;searchPhrase=groovy&amp;amp;searchType=session&amp;amp;tc=0&amp;amp;sortBy=titleSort&amp;amp;p=&amp;amp;i(11424)=&amp;amp;i(10050)=&amp;amp;i(10090)=&amp;amp;i(10092)=&amp;amp;i(11842)=&amp;amp;i(10086)=">tons of sessions and BOF speaking about Groovy&lt;/a> or mentioning it, and where I&amp;rsquo;ll be participating at two sessions, one of the novelties of Groovy 2 and the ScriptBowl.&lt;/p>
&lt;p>Let me highlight a few of those sessions:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=6505">What&amp;rsquo;s new in Groovy 2&lt;/a>&lt;br />
Shameless plug, that&amp;rsquo;s my session, where I speak about some of the key features of Groovy 1.8, and then quickly delve into the major features of Groovy 2&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=7945">What&amp;rsquo;s new in Grails 2&lt;/a>&lt;br />
Graeme Rocher will cover and demo the key features of Grails 2.x&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=5106">Griffon, up close and personal&lt;/a>&lt;br />
Andres Almiray will tell us all about the Griffon rich desktop application framework&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=5100">The Groovy Ecosystem&lt;/a>&lt;br />
Again, Andres on stage here, to speak about the many projects built atop Groovy, for your build, for testing and more&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=5102">Walk through Groovy&amp;rsquo;s AST transformations&lt;/a>&lt;br />
Andres walks us through Groovy&amp;rsquo;s mechanism to plugin into the compiler to bring powerful compile-time metaprogramming abilities to your classes&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=3469">Design patterns in Groovy&lt;/a>&lt;br />
Venkat Subramaniam revisits classical design patterns as well as new ones&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=4857">Improving your Groovy kung-fu&lt;/a>&lt;br />
Dierk König highlight key features of the Groovy language and nice library you can leverage to make you a better and more productive Groovy developer&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=5045">A common development framework, from JSP to GSP, at CERN&lt;/a>&lt;br />
The CERN research center (the great hadron collider, etc) talk about their usage of Groovy and Grails, with concrete examples, screenshots and more&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=2813">GroovyFX: making JavaFX Groovy&lt;/a>&lt;br />
Jim Clarke and Dierk talk about the GroovyFX library, a thin DSL on top of JavaFX to express your creativity with JavaFX in a nice Groovy idiom&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=5486">Grezi and GroovyFX&lt;/a>&lt;br />
Bertrand Goetzmann and Stephen Chin speak about GroovyFX and show a concrete usage with the Grezi framework, to build powerful visual and interactive presentations&lt;/li>
&lt;li>&lt;a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=3257">ScriptBowl: the battle of the JVM based languages&lt;/a>&lt;br />
I&amp;rsquo;ll finish with the ScriptBowl, a friendly 2-round battle comparing some of the popular languages of the JVM platform. Hopefully, I&amp;rsquo;ll try to win again this year, but the competition is tough! Fingers crossed! Come in number to support me and make Groovy win again!&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>Monday I&rsquo;ll be flying to San Francisco for the JavaOne conference, where there will be <a href="https://oracleus.activeevents.com/connect/search.ww?event=javaone#loadSearch-event=javaone&amp;searchPhrase=groovy&amp;searchType=session&amp;tc=0&amp;sortBy=titleSort&amp;p=&amp;i(11424)=&amp;i(10050)=&amp;i(10090)=&amp;i(10092)=&amp;i(11842)=&amp;i(10086)=">tons of sessions and BOF speaking about Groovy</a> or mentioning it, and where I&rsquo;ll be participating at two sessions, one of the novelties of Groovy 2 and the ScriptBowl.</p>
<p>Let me highlight a few of those sessions:</p>
<ul>
<li><a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=6505">What&rsquo;s new in Groovy 2</a><br />
Shameless plug, that&rsquo;s my session, where I speak about some of the key features of Groovy 1.8, and then quickly delve into the major features of Groovy 2</li>
<li><a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=7945">What&rsquo;s new in Grails 2</a><br />
Graeme Rocher will cover and demo the key features of Grails 2.x</li>
<li><a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=5106">Griffon, up close and personal</a><br />
Andres Almiray will tell us all about the Griffon rich desktop application framework</li>
<li><a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=5100">The Groovy Ecosystem</a><br />
Again, Andres on stage here, to speak about the many projects built atop Groovy, for your build, for testing and more</li>
<li><a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=5102">Walk through Groovy&rsquo;s AST transformations</a><br />
Andres walks us through Groovy&rsquo;s mechanism to plugin into the compiler to bring powerful compile-time metaprogramming abilities to your classes</li>
<li><a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=3469">Design patterns in Groovy</a><br />
Venkat Subramaniam revisits classical design patterns as well as new ones</li>
<li><a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=4857">Improving your Groovy kung-fu</a><br />
Dierk König highlight key features of the Groovy language and nice library you can leverage to make you a better and more productive Groovy developer</li>
<li><a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=5045">A common development framework, from JSP to GSP, at CERN</a><br />
The CERN research center (the great hadron collider, etc) talk about their usage of Groovy and Grails, with concrete examples, screenshots and more</li>
<li><a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=2813">GroovyFX: making JavaFX Groovy</a><br />
Jim Clarke and Dierk talk about the GroovyFX library, a thin DSL on top of JavaFX to express your creativity with JavaFX in a nice Groovy idiom</li>
<li><a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=5486">Grezi and GroovyFX</a><br />
Bertrand Goetzmann and Stephen Chin speak about GroovyFX and show a concrete usage with the Grezi framework, to build powerful visual and interactive presentations</li>
<li><a href="https://oracleus.activeevents.com/connect/sessionDetail.ww?SESSION_ID=3257">ScriptBowl: the battle of the JVM based languages</a><br />
I&rsquo;ll finish with the ScriptBowl, a friendly 2-round battle comparing some of the popular languages of the JVM platform. Hopefully, I&rsquo;ll try to win again this year, but the competition is tough! Fingers crossed! Come in number to support me and make Groovy win again!</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.0.4 is out</title><link>https://glaforge.dev/posts/2012/09/21/groovy-2-0-4-is-out/</link><pubDate>Fri, 21 Sep 2012 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/09/21/groovy-2-0-4-is-out/</guid><description>&lt;p>The Groovy development team&amp;rsquo;s just &lt;strong>released Groovy 2.0.4&lt;/strong>, a bug fix for our Groovy 2.0 branch.&lt;/p>
&lt;p>It fixes some important issues we&amp;rsquo;ve had with generics with the stub generator, as well as several fixes related to the static type checking and static compilation features. We also worked on the recompilation issues with the GroovyScriptEngine.&lt;/p>
&lt;p>You can download Groovy 2.0.4 here: &lt;a href="http://groovy.codehaus.org/Download">http://groovy.codehaus.org/Download&lt;/a>&lt;/p>
&lt;p>The artifacts are not yet on Maven Central, but will shortly.&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy development team&rsquo;s just <strong>released Groovy 2.0.4</strong>, a bug fix for our Groovy 2.0 branch.</p>
<p>It fixes some important issues we&rsquo;ve had with generics with the stub generator, as well as several fixes related to the static type checking and static compilation features. We also worked on the recompilation issues with the GroovyScriptEngine.</p>
<p>You can download Groovy 2.0.4 here: <a href="http://groovy.codehaus.org/Download">http://groovy.codehaus.org/Download</a></p>
<p>The artifacts are not yet on Maven Central, but will shortly.</p>
<p>Also, the online JavaDoc and GroovyDoc are still being uploaded as I write this, but will hopefully be online in a few hours.</p>
<p>You can have a look at the issues fixed in this release on JIRA: <a href="https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=18777">https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=18777</a></p>
<p>If you&rsquo;re curious why we&rsquo;ve jumped from 2.0.2 to 2.0.4, skipping 2.0.3, it&rsquo;s because of some issues with the release of 2.0.3 which I had mistakenly built with JDK 6 instead of JDK 7, which ruled out all our invoke dynamic support. So we quickly moved to 2.0.4.</p>
<p>We&rsquo;ll certainly also release a 1.8.9 soon, especially for the stub generator fixes which might be useful for those affected with the issues we&rsquo;ve fixed.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.0.2 and 1.8.8</title><link>https://glaforge.dev/posts/2012/09/10/groovy-2-0-2-and-1-8-8/</link><pubDate>Mon, 10 Sep 2012 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/09/10/groovy-2-0-2-and-1-8-8/</guid><description>&lt;p>I&amp;rsquo;ll echo here the two new releases of the &lt;a href="http://groovy.codehaus.org">Groovy programming language&lt;/a>:&lt;/p>
&lt;blockquote>
&lt;p>This is with great pleasure that the Groovy development team announces the &lt;strong>releases of Groovy 2.0.2 and Groovy 1.8.8&lt;/strong>.&lt;/p>
&lt;p>Those two releases are essentially just &lt;strong>bug fix releases&lt;/strong>, with only minor improvements. In particular, work has been done in the area of the static type checker and static compilation, in the GroovyScriptEngine, as well as further performance improvements for the &amp;ldquo;invoke dynamic&amp;rdquo; support as well as plain Groovy. We also upgraded the version of GPars of the distribution to use the newly released 1.0-beta-3.&lt;/p></description><content:encoded>
<![CDATA[<p>I&rsquo;ll echo here the two new releases of the <a href="http://groovy.codehaus.org">Groovy programming language</a>:</p>
<blockquote>
<p>This is with great pleasure that the Groovy development team announces the <strong>releases of Groovy 2.0.2 and Groovy 1.8.8</strong>.</p>
<p>Those two releases are essentially just <strong>bug fix releases</strong>, with only minor improvements. In particular, work has been done in the area of the static type checker and static compilation, in the GroovyScriptEngine, as well as further performance improvements for the &ldquo;invoke dynamic&rdquo; support as well as plain Groovy. We also upgraded the version of GPars of the distribution to use the newly released 1.0-beta-3.</p>
<p>For the details of both releases, please have a look at the JIRA changelog here: for Groovy 2.0.2: <a href="http://bit.ly/gr202">http://bit.ly/gr202</a>, and for Groovy 1.8.8: <a href="http://bit.ly/gr188">http://bit.ly/gr188</a><br />
As usual, you can **download Groovy **in our download area: <a href="http://groovy.codehaus.org/Download">http://groovy.codehaus.org/Download</a></p>
<p>Thanks a lot to all those who contributed to those releases.</p>
<p>I&rsquo;d also like to seize this opportunity to highlight our contribution page, to those who are <strong>interested in contributing to Groovy</strong>: <a href="http://groovy.codehaus.org/Contributing">http://groovy.codehaus.org/Contributing</a></p>
<p>Going forward, we&rsquo;re going to propose a list of JIRA issues you might have a look at, if you want to get started working on the Groovy project: <a href="http://bit.ly/gcontrib">http://bit.ly/gcontrib</a></p>
<p>And on the contribution page mentioned above, at the bottom of the page, we feature a table containing wider goals or side projects that you might be interested in contributing to.</p>
<p>So don&rsquo;t hesitate to come back to us with ideas or with questions regarding those possible areas of contributions, and we&rsquo;ll gladly welcome your help!</p></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Also back to vi... MacVim actually</title><link>https://glaforge.dev/posts/2012/08/09/also-back-to-vi-macvim-actually/</link><pubDate>Thu, 09 Aug 2012 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/08/09/also-back-to-vi-macvim-actually/</guid><description>&lt;p>Some of my friends are going back to vi like me, for instance Hibernate&amp;rsquo;s &lt;a href="http://emmanuelbernard.com/blog/2012/08/01/i-succumbed-to-the-cult-of-vi/">Emmanuel&lt;/a> and IzPack&amp;rsquo;s &lt;a href="http://log.julien.ponge.info/post/28914211607/tweaking-macvim">Julien&lt;/a>. I also mentioned a few good links about that move on the French &lt;a href="http://lescastcodeurs.com/2012/07/19/les-cast-codeurs-podcast-episode-62-rod-va-sur-l-ile-de-larry/">Les Castcodeurs podcast&lt;/a>. And to be precise, I&amp;rsquo;m using &lt;a href="http://code.google.com/p/macvim/">MacVim&lt;/a> on my MacBook Pro laptop, but also vim on the command-line.&lt;/p>
&lt;p>Emmanuel and Julien gave some good tips and links too, and like Julien, I&amp;rsquo;m going to show you my current &lt;code>.vimrc&lt;/code> in case anyone&amp;rsquo;s interested:&lt;/p></description><content:encoded>
<![CDATA[<p>Some of my friends are going back to vi like me, for instance Hibernate&rsquo;s <a href="http://emmanuelbernard.com/blog/2012/08/01/i-succumbed-to-the-cult-of-vi/">Emmanuel</a> and IzPack&rsquo;s <a href="http://log.julien.ponge.info/post/28914211607/tweaking-macvim">Julien</a>. I also mentioned a few good links about that move on the French <a href="http://lescastcodeurs.com/2012/07/19/les-cast-codeurs-podcast-episode-62-rod-va-sur-l-ile-de-larry/">Les Castcodeurs podcast</a>. And to be precise, I&rsquo;m using <a href="http://code.google.com/p/macvim/">MacVim</a> on my MacBook Pro laptop, but also vim on the command-line.</p>
<p>Emmanuel and Julien gave some good tips and links too, and like Julien, I&rsquo;m going to show you my current <code>.vimrc</code> in case anyone&rsquo;s interested:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">let</span> mapleader <span style="color:#666">=</span> <span style="color:#4070a0">&#34;,&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">let</span> g<span style="color:#666">:</span>mapleader<span style="color:#666">=</span><span style="color:#4070a0">&#34;,&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">if</span> has(<span style="color:#4070a0">&#34;gui_running&#34;</span>)
</span></span><span style="display:flex;"><span>   color solarized
</span></span><span style="display:flex;"><span>endif
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>set background<span style="color:#666">=</span>dark
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>set number
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>set expandtab
</span></span><span style="display:flex;"><span>set shiftwidth<span style="color:#666">=</span><span style="color:#40a070">4</span>
</span></span><span style="display:flex;"><span>set tabstop<span style="color:#666">=</span><span style="color:#40a070">4</span>
</span></span><span style="display:flex;"><span>set smarttab
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>set ai <span style="color:#4070a0">&#34;Auto indent
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">set si &#34;</span>Smart indent
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>set ruler
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>set hlsearch
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>set nocompatible
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>set bs<span style="color:#666">=</span><span style="color:#40a070">2</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>set autoread
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>set so<span style="color:#666">=</span><span style="color:#40a070">7</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>filetype on
</span></span><span style="display:flex;"><span>filetype indent on
</span></span><span style="display:flex;"><span>filetype plugin on
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>set guifont<span style="color:#666">=</span>Inconsolata<span style="color:#666">:</span>h16
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>autocmd<span style="color:#666">!</span> bufwritepost .vimrc source <span style="color:#666">~</span><span style="">/.vimrc</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>syntax enable
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>au BufNewFile,BufRead <span style="color:#666">*</span>.gradle set filetype<span style="color:#666">=</span>groovy
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">function</span><span style="color:#666">!</span> RunGroovy()
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">let</span> gfname<span style="color:#666">=</span><span style="">@</span><span style="color:#666">%</span>
</span></span><span style="display:flex;"><span>    botright <span style="color:#007020;font-weight:bold">new</span>
</span></span><span style="display:flex;"><span>    setlocal buftype<span style="color:#666">=</span>nofile bufhidden<span style="color:#666">=</span>wipe nobuflisted noswapfile nowrap
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">let</span> gtmpf <span style="color:#666">=</span> tempname()
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">let</span> gcmd <span style="color:#666">=</span> <span style="color:#4070a0">&#39;:!groovy &#39;</span> . gfname . <span style="color:#4070a0">&#39; &gt; &#39;</span> . gtmpf
</span></span><span style="display:flex;"><span>    silent execute gcmd
</span></span><span style="display:flex;"><span>    silent execute <span style="color:#4070a0">&#39;0r &#39;</span> . gtmpf
</span></span><span style="display:flex;"><span>endfunction
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>map   <span style="color:#666">:</span>call RunGroovy()
</span></span><span style="display:flex;"><span>nmap <span style="">@</span> <span style="color:#666">:</span>NERDTree
</span></span></code></pre></div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Three joint releases of Groovy!</title><link>https://glaforge.dev/posts/2012/07/25/three-joint-releases-of-groovy/</link><pubDate>Wed, 25 Jul 2012 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/07/25/three-joint-releases-of-groovy/</guid><description>&lt;p>The Groovy development team is happy to announce the &lt;strong>releases of Groovy 2.0.1, 1.8.7 and 1.7.11&lt;/strong>! That&amp;rsquo;s the first time we release three versions at the same time.&lt;/p>
&lt;p>Those three versions are essentially just bug fix releases. The 1.8.7 release contains a lot of the bugfixes that were already integrated in Groovy 2.0.0, so it&amp;rsquo;s just an alignment with the Groovy 2 branch. In the future, Groovy 1.8.x and Groovy 2.x will be maintained in parallel. But the Groovy 1.7 branch is now in end-of-life and won&amp;rsquo;t be maintained further.&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy development team is happy to announce the <strong>releases of Groovy 2.0.1, 1.8.7 and 1.7.11</strong>! That&rsquo;s the first time we release three versions at the same time.</p>
<p>Those three versions are essentially just bug fix releases. The 1.8.7 release contains a lot of the bugfixes that were already integrated in Groovy 2.0.0, so it&rsquo;s just an alignment with the Groovy 2 branch. In the future, Groovy 1.8.x and Groovy 2.x will be maintained in parallel. But the Groovy 1.7 branch is now in end-of-life and won&rsquo;t be maintained further.</p>
<p>In terms of improvements, you might notice some performance gains with the &ldquo;invoke dynamic&rdquo; version, or you&rsquo;ll notice that you can use the Groovy Console in full-screen mode on Mac OS X.</p>
<p>A lot of attention has particularly been put into the bug fixes of the new features of Groovy 2.0. In particular, the static type checking and static compilation features got the lion share of fixes.</p>
<p>We also restored the OSGi manifest information. So if you&rsquo;re using OSGi, please report back to us if you see any remaining quirk when using Groovy in an OSGi container. However, there&rsquo;s still a known issue for the &ldquo;invoke dynamic&rdquo; JARs of Groovy, as neither the OSGi Gradle plugin nor the underlying bnd library have been updated to cope with the &ldquo;invoke dynamic&rdquo; bytecode instruction, so we&rsquo;ll have to wait for the releases of both before being able to get proper OSGi support in our &ldquo;indy&rdquo; deliverables.</p>
<p>You can download those latest Groovy versions from here: <a href="http://groovy.codehaus.org/Download">http://groovy.codehaus.org/Download</a></p>
<p>And here are the JIRA release notes for 2.0.1 in particular (but you&rsquo;ll find the notes for 1.8.7 and 1.7.11 on the download page too): <a href="https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=18599">https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=18599</a></p>
<p>Thanks a lot for your attention, and big thanks to all those who contributed to those releases!</p>
<p>Keep on groovying!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Algorithms for collaborative editing</title><link>https://glaforge.dev/posts/2012/07/09/algorithms-for-collaborative-editing/</link><pubDate>Mon, 09 Jul 2012 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/07/09/algorithms-for-collaborative-editing/</guid><description>&lt;p>Over the weekend, I was brainstorming some ideas about what a second generation &lt;a href="http://groovyconsole.appspot.com/">Groovy Web Console&lt;/a> could look like. I was particularly thinking about collaborative editing Groovy snippets of code in the cloud.&lt;/p>
&lt;p>There&amp;rsquo;s a trend towards IDEs in the Cloud, like for instance &lt;a href="http://cloud-ide.com/">Cloud-IDE&lt;/a>, allowing you to work on your projects remotely, on the cloud, from the comfort of your browser, even letting you push new versions of your apps in your favorite PaaS.&lt;/p></description><content:encoded>
<![CDATA[<p>Over the weekend, I was brainstorming some ideas about what a second generation <a href="http://groovyconsole.appspot.com/">Groovy Web Console</a> could look like. I was particularly thinking about collaborative editing Groovy snippets of code in the cloud.</p>
<p>There&rsquo;s a trend towards IDEs in the Cloud, like for instance <a href="http://cloud-ide.com/">Cloud-IDE</a>, allowing you to work on your projects remotely, on the cloud, from the comfort of your browser, even letting you push new versions of your apps in your favorite PaaS.</p>
<p>And just a few days ago, another interesting project in this field was open-sourced: <a href="http://code.google.com/p/collide/">Collide</a>. It&rsquo;s a collaborative IDE running on <a href="http://vertx.io/">Vert.x</a>, using the <a href="http://en.wikipedia.org/wiki/Operational_transformation">Operational Transform</a> approach popularized by the defunct Google Wave project whose remains are <a href="http://incubator.apache.org/wave/">incubating at Apache</a>. Funnily, it&rsquo;s yet again the remains of some internal project from Google that got axed as <a href="https://plus.google.com/u/0/114130972232398734985/posts/82BktQAKzcN">explained on Google+</a>.</p>
<p>As I was interested in the topic, I wanted to learn a bit more about the technologies and algorithms behind that. That&rsquo;s why I wanted to post here my findings, in case someone&rsquo;s interested and for future reference shall I dive deeper into that matter! I&rsquo;m not covering the details of each algorithm, but instead, I&rsquo;m giving links that you can harvest for further information.</p>
<p>When working collaboratively on the same document, instead of sending the whole document with your changes to the other collaborators, you usually just send differences through the wire. Probably the most used <a href="http://en.wikipedia.org/wiki/Diff">diff algorithm</a> is the <a href="http://www.xmailserver.org/diff2.pdf">one from Eugene Myers</a>.</p>
<p>Using the diff approach, techniques like <a href="http://neil.fraser.name/writing/sync/">differential synchronization</a> using <a href="http://code.google.com/p/google-diff-match-patch/">diff / match / patch</a> are interesting (it&rsquo;s using the diff algorithm from Myers above). Neil Fraser (from Google again) gives some more <a href="http://neil.fraser.name/writing/sync/">details on the approach</a>.</p>
<p>So let&rsquo;s continue our journey with <a href="http://en.wikipedia.org/wiki/Operational_transformation">Operational Transformations</a>, called OT for short. As I said earlier, it&rsquo;s been used in Google Wave. Basically, each client connected to a common document send units of changes like an insertion of (some) character(s), or a deletion. Because of latency between the time you receive the difference and the moment you&rsquo;ve made some changes locally, you need to transform the change you&rsquo;ve received so it can still be applied to your own copy of the document.</p>
<p>In order to know in which order a particular change happened before the other (so that you can apply the changes as they should), there are also specific algorithms. For one, <a href="http://en.wikipedia.org/wiki/Lamport_timestamps">Lamport timestamps</a> can be used to keep track of that precedence or causality property.</p>
<p>But you might more likely have heard of <a href="http://en.wikipedia.org/wiki/Vector_clock">vector clocks</a> to maintain that happen-before relation between events, as this algorithm is often used by the various NoSQL datastores forconsistency purpose. I found an implementation of <a href="http://code.google.com/p/project-voldemort/source/browse/trunk/src/java/voldemort/versioning/VectorClock.java?r=170">vector clocks in voldemort</a>, as well as in this <a href="https://code.google.com/p/tud-in4150-fp-ass2b/source/browse/trunk/src/in4150/mutex/VectorClock.java">Google code project</a>. There&rsquo;s also a brief article on the <a href="http://skipperkongen.dk/2011/07/26/vector-clocks-vs-lamport-timestamps/">superiority of vector clocks over Lamport timestamps</a> as they give more information about the precedence.</p>
<p>Last but not least, it seems that another approach is even more interesting: interval tree clocks (for which I&rsquo;ve found a <a href="http://code.google.com/p/itc4j/">small library here</a> implementing it). Vector clocks are great but seem to need more memory to hold the state and don&rsquo;t accomodate too well when the number of participants is changing, and interval tree clocks seem to address that problem.</p>
<p>The journey ends here with those few links to different algorithms and code samples. Perhaps I&rsquo;ll explore more an approach or the other later on if time permits. Happy reading!</p>
<p>Don&rsquo;t hesitate to share your own experience, links, corrections, etc, in the comments below.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Minor new features of Groovy 2.0</title><link>https://glaforge.dev/posts/2012/07/04/minor-new-features-of-groovy-2-0/</link><pubDate>Wed, 04 Jul 2012 04:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/07/04/minor-new-features-of-groovy-2-0/</guid><description>&lt;p>Although I&amp;rsquo;ve gone at length through the &lt;a href="http://bitly.com/groovy2">key major features of Groovy 2.0&lt;/a>in the InfoQ article, there have been a few smaller ones which are interesting to note. And groovy guys like &lt;a href="http://blog.bloidonia.com/">Tim Yates&lt;/a>, &lt;a href="http://blog.andresteingress.com/">André Steingress&lt;/a> or &lt;a href="http://mrhaki.blogspot.fr/">Mr Hakki&lt;/a> were quick to blog about them!&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://blog.bloidonia.com/post/26065857945/whats-new-in-groovy-2-0-inject-with-default">&lt;code>inject()&lt;/code> method with a default initial value&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://blog.bloidonia.com/post/26065074691/whats-new-in-groovy-2-0-takewhile-and-dropwhile">&lt;code>takeWhile()&lt;/code> and &lt;code>dropWhile()&lt;/code> methods&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://blog.andresteingress.com/2012/06/29/groovy-2-0-love-for-grails-command-objects/">&lt;code>withDefault()&lt;/code>, &lt;code>withEager()&lt;/code> methods&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://mrhaki.blogspot.fr/2012/06/groovy-goodness-partial-matches.html">&lt;code>matchesPartially()&lt;/code> method for matchers&lt;/a> (to see if a string might match a pattern given more input)&lt;/li>
&lt;li>&lt;a href="http://blog.andresteingress.com/2012/03/04/using-notyetimplemented-in-test-cases/">&lt;code>@NotYetImplemented&lt;/code> transformation for test cases&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://jira.codehaus.org/browse/GROOVY-5387">an iterable &lt;code>collectEntries()&lt;/code> variant&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://jira.codehaus.org/browse/GROOVY-5407">&lt;code>first()&lt;/code> and &lt;code>last()&lt;/code> working with iterables as well&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://jira.codehaus.org/browse/GROOVY-5283">&lt;code>collate()&lt;/code> method&lt;/a> (although it&amp;rsquo;s also in 1.8.6 actually) (see &lt;a href="http://blog.bloidonia.com/post/18073244930/whats-new-in-groovy-1-8-6-the-collate-method">coverage by Tim&lt;/a> and &lt;a href="http://mrhaki.blogspot.co.uk/2012/04/groovy-goodness-collate-list-into-sub.html">Mr Hakki&lt;/a>)&lt;/li>
&lt;li>&lt;a href="https://jira.codehaus.org/browse/GROOVY-3916">calendar ranges&lt;/a> (useful for iterating over days of a certain period)&lt;/li>
&lt;/ul>
&lt;p>Those are some smaller features compared to the big highlights of Groovy 2.0, but they are definitely useful and make you more productive every day!&lt;/p></description><content:encoded>
<![CDATA[<p>Although I&rsquo;ve gone at length through the <a href="http://bitly.com/groovy2">key major features of Groovy 2.0</a>in the InfoQ article, there have been a few smaller ones which are interesting to note. And groovy guys like <a href="http://blog.bloidonia.com/">Tim Yates</a>, <a href="http://blog.andresteingress.com/">André Steingress</a> or <a href="http://mrhaki.blogspot.fr/">Mr Hakki</a> were quick to blog about them!</p>
<ul>
<li><a href="http://blog.bloidonia.com/post/26065857945/whats-new-in-groovy-2-0-inject-with-default"><code>inject()</code> method with a default initial value</a></li>
<li><a href="http://blog.bloidonia.com/post/26065074691/whats-new-in-groovy-2-0-takewhile-and-dropwhile"><code>takeWhile()</code> and <code>dropWhile()</code> methods</a></li>
<li><a href="http://blog.andresteingress.com/2012/06/29/groovy-2-0-love-for-grails-command-objects/"><code>withDefault()</code>, <code>withEager()</code> methods</a></li>
<li><a href="http://mrhaki.blogspot.fr/2012/06/groovy-goodness-partial-matches.html"><code>matchesPartially()</code> method for matchers</a> (to see if a string might match a pattern given more input)</li>
<li><a href="http://blog.andresteingress.com/2012/03/04/using-notyetimplemented-in-test-cases/"><code>@NotYetImplemented</code> transformation for test cases</a></li>
<li><a href="https://jira.codehaus.org/browse/GROOVY-5387">an iterable <code>collectEntries()</code> variant</a></li>
<li><a href="https://jira.codehaus.org/browse/GROOVY-5407"><code>first()</code> and <code>last()</code> working with iterables as well</a></li>
<li><a href="https://jira.codehaus.org/browse/GROOVY-5283"><code>collate()</code> method</a> (although it&rsquo;s also in 1.8.6 actually) (see <a href="http://blog.bloidonia.com/post/18073244930/whats-new-in-groovy-1-8-6-the-collate-method">coverage by Tim</a> and <a href="http://mrhaki.blogspot.co.uk/2012/04/groovy-goodness-collate-list-into-sub.html">Mr Hakki</a>)</li>
<li><a href="https://jira.codehaus.org/browse/GROOVY-3916">calendar ranges</a> (useful for iterating over days of a certain period)</li>
</ul>
<p>Those are some smaller features compared to the big highlights of Groovy 2.0, but they are definitely useful and make you more productive every day!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Building your Groovy 2.0 projects with Maven</title><link>https://glaforge.dev/posts/2012/07/04/building-your-groovy-2-0-projects-with-maven/</link><pubDate>Wed, 04 Jul 2012 02:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/07/04/building-your-groovy-2-0-projects-with-maven/</guid><description>&lt;p>There are various options for building your Groovy projects: including Ant with the &lt;a href="http://groovy.codehaus.org/The+groovyc+Ant+Task">Groovyc Ant task&lt;/a>, or with &lt;a href="http://www.gradle.org/docs/current/userguide/groovy_plugin.html">Gradle Groovy plugin&lt;/a>. But today, I&amp;rsquo;d like to speak about Maven. You have actually two possible choices: &lt;a href="http://gmaven.codehaus.org/">GMaven&lt;/a> or the &lt;a href="http://groovy.codehaus.org/Groovy-Eclipse+compiler+plugin+for+Maven">Groovy Eclipse Maven plugin&lt;/a>.&lt;/p>
&lt;h2 id="gmaven">GMaven&lt;/h2>
&lt;p>GMaven 1.4 already allowed you to use Groovy 2.0 even before the final release of Groovy, although a new 1.5 version is about to be released soon, which should also include the option to use the &amp;ldquo;invoke dynamic&amp;rdquo; support which is currently missing for GMaven 1.4.&lt;/p></description><content:encoded>
<![CDATA[<p>There are various options for building your Groovy projects: including Ant with the <a href="http://groovy.codehaus.org/The+groovyc+Ant+Task">Groovyc Ant task</a>, or with <a href="http://www.gradle.org/docs/current/userguide/groovy_plugin.html">Gradle Groovy plugin</a>. But today, I&rsquo;d like to speak about Maven. You have actually two possible choices: <a href="http://gmaven.codehaus.org/">GMaven</a> or the <a href="http://groovy.codehaus.org/Groovy-Eclipse+compiler+plugin+for+Maven">Groovy Eclipse Maven plugin</a>.</p>
<h2 id="gmaven">GMaven</h2>
<p>GMaven 1.4 already allowed you to use Groovy 2.0 even before the final release of Groovy, although a new 1.5 version is about to be released soon, which should also include the option to use the &ldquo;invoke dynamic&rdquo; support which is currently missing for GMaven 1.4.</p>
<p>Keegan Witt, the lead of the GMaven project, just recently <a href="http://groovy.329449.n5.nabble.com/Groovy-Eclipse-compiler-plugin-for-Maven-replacing-gmaven-td5709858.html#a5710494">posted an example POM to configure GMaven with Groovy 2.0</a>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;properties&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;gmavenVersion&gt;</span>1.4<span style="color:#062873;font-weight:bold">&lt;/gmavenVersion&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;gmavenProviderSelection&gt;</span>2.0<span style="color:#062873;font-weight:bold">&lt;/gmavenProviderSelection&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;groovyVersion&gt;</span>2.0.0<span style="color:#062873;font-weight:bold">&lt;/groovyVersion&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/properties&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;dependencies&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>org.codehaus.groovy<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>groovy-all<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>${groovyVersion}<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/dependencies&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;build&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;plugins&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;plugin&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>org.codehaus.gmaven<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>gmaven-plugin<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>${gmavenVersion}<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;configuration&gt;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&lt;providerSelection&gt;</span>${gmavenProviderSelection}<span style="color:#062873;font-weight:bold">&lt;/providerSelection&gt;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&lt;sourceEncoding&gt;</span>UTF-8<span style="color:#062873;font-weight:bold">&lt;/sourceEncoding&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;/configuration&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;executions&gt;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&lt;execution&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;goals&gt;</span>
</span></span><span style="display:flex;"><span>              <span style="color:#062873;font-weight:bold">&lt;goal&gt;</span>generateStubs<span style="color:#062873;font-weight:bold">&lt;/goal&gt;</span>
</span></span><span style="display:flex;"><span>              <span style="color:#062873;font-weight:bold">&lt;goal&gt;</span>compile<span style="color:#062873;font-weight:bold">&lt;/goal&gt;</span>
</span></span><span style="display:flex;"><span>              <span style="color:#062873;font-weight:bold">&lt;goal&gt;</span>generateTestStubs<span style="color:#062873;font-weight:bold">&lt;/goal&gt;</span>
</span></span><span style="display:flex;"><span>              <span style="color:#062873;font-weight:bold">&lt;goal&gt;</span>testCompile<span style="color:#062873;font-weight:bold">&lt;/goal&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#062873;font-weight:bold">&lt;/goals&gt;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#062873;font-weight:bold">&lt;/execution&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;/executions&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;dependencies&gt;</span>
</span></span><span style="display:flex;"><span>         <span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>           <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>org.codehaus.groovy<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>           <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>groovy-all<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>           <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>${groovyVersion}<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>         <span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span><span style="display:flex;"><span>       <span style="color:#062873;font-weight:bold">&lt;/dependencies&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;/plugin&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;/plugins&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/build&gt;</span>
</span></span></code></pre></div><p>Stay tuned for version 1.5 of the GMaven plugin.</p>
<h2 id="groovy-eclipse-compiler-plugin-for-maven">Groovy Eclipse compiler plugin for Maven</h2>
<p>The alternative solution is to use the compiler from the Groovy Eclipse project, which can be used as a Maven plugin. Not long ago, I changed a Maven-powered project to use Groovy Eclipse instead of GMaven, and it worked flawlessly, and I was quite happy with the result.</p>
<p>The <a href="http://groovy.codehaus.org/Groovy-Eclipse+compiler+plugin+for+Maven">Groovy Eclipse compiler plugin for Maven</a> is very well documented, so be sure to have a look at it for all the details on how to best configure it.</p>
<p>In the meantime, the simplest way to build your Groovy 2.0 sources can be achieved with this simpler configuration, as it&rsquo;s using 2.0 by default:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;build&gt;</span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;plugins&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;plugin&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>maven-compiler-plugin<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>2.3.2<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;configuration&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;compilerId&gt;</span>groovy-eclipse-compiler<span style="color:#062873;font-weight:bold">&lt;/compilerId&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;/configuration&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;dependencies&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>org.codehaus.groovy<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>groovy-eclipse-compiler<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>2.7.0-01<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;/dependencies&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;/plugin&gt;</span>
</span></span><span style="display:flex;"><span>  ...
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/plugins&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/build&gt;</span>
</span></span></code></pre></div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.0 support in Eclipse</title><link>https://glaforge.dev/posts/2012/07/04/groovy-2-0-support-in-eclipse/</link><pubDate>Tue, 03 Jul 2012 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/07/04/groovy-2-0-support-in-eclipse/</guid><description>&lt;p>Right after the &lt;a href="https://glaforge.dev/posts/2012/06/28/groovy-2-0-released/">release of Groovy 2.0&lt;/a>, our intrepid Eclipse team released a new version of the Groovy Eclipse plugin. The big highlights are, of course, the Groovy 2.0 support, but also the support of Juno and the move of the sources of the plugin to GitHub, plus some new refactorings and quickfixes.&lt;br />
Here&amp;rsquo;s &lt;a href="http://contraptionsforprogramming.blogspot.fr/">Andrew&lt;/a>&amp;rsquo;s full announcement:&lt;/p>
&lt;blockquote>
&lt;p>Hot on the heels of Groovy 2.0, we are pleased to announce the release of Groovy-Eclipse 2.7.0.&lt;/p></description><content:encoded>
<![CDATA[<p>Right after the <a href="https://glaforge.dev/posts/2012/06/28/groovy-2-0-released/">release of Groovy 2.0</a>, our intrepid Eclipse team released a new version of the Groovy Eclipse plugin. The big highlights are, of course, the Groovy 2.0 support, but also the support of Juno and the move of the sources of the plugin to GitHub, plus some new refactorings and quickfixes.<br />
Here&rsquo;s <a href="http://contraptionsforprogramming.blogspot.fr/">Andrew</a>&rsquo;s full announcement:</p>
<blockquote>
<p>Hot on the heels of Groovy 2.0, we are pleased to announce the release of Groovy-Eclipse 2.7.0.</p>
<p>The major parts to this release are our Groovy 2.0 support, Eclipse Juno support, and our move to github. We&rsquo;ve also implemented a few new refactorings and quickfixes. You can find the glorious details here: <a href="http://docs.codehaus.org/display/GROOVY/Groovy-Eclipse+2.7.0+New+and+Noteworthy">http://docs.codehaus.org/display/GROOVY/Groovy-Eclipse+2.7.0+New+and+Noteworthy</a></p>
<p>We have also released v2.7.0 of the groovy-eclipse-compiler plugin for maven. This version now has Groovy 2.0 support.</p>
<p>Thanks everyone for trying out snapshot releases and raising issues. Please keep the feedback coming!</p></blockquote>
<p>Get it while it&rsquo;s hot! And be sure to read what&rsquo;s new in the link above for all the details.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>What's new in Groovy 2.0</title><link>https://glaforge.dev/posts/2012/06/28/whats-new-in-groovy-20/</link><pubDate>Thu, 28 Jun 2012 15:00:39 +0100</pubDate><guid>https://glaforge.dev/posts/2012/06/28/whats-new-in-groovy-20/</guid><description>&lt;p>The newly released Groovy 2.0 brings key static features to the language with &lt;strong>static type checking&lt;/strong> and &lt;strong>static compilation&lt;/strong>, adopts JDK 7 related improvements with &lt;strong>Project Coin syntax enhancements&lt;/strong> and the &lt;strong>support of the new &amp;ldquo;invoke dynamic&amp;rdquo;&lt;/strong> JVM instruction, and becomes &lt;strong>more modular&lt;/strong> than before. In this article, we&amp;rsquo;re going to look into those new features in more detail.&lt;/p>
&lt;h2 id="a-static-theme-for-a-dynamic-language">A &amp;ldquo;static theme&amp;rdquo; for a dynamic language&lt;/h2>
&lt;h2 id="static-type-checking">Static type checking&lt;/h2>
&lt;p>Groovy, by nature, is and will always be a dynamic language. However, Groovy is often used as a &amp;ldquo;Java scripting language&amp;rdquo;, or as a &amp;ldquo;better Java&amp;rdquo; (ie. a Java with less boilerplate and more power features).&lt;/p></description><content:encoded>
<![CDATA[<p>The newly released Groovy 2.0 brings key static features to the language with <strong>static type checking</strong> and <strong>static compilation</strong>, adopts JDK 7 related improvements with <strong>Project Coin syntax enhancements</strong> and the <strong>support of the new &ldquo;invoke dynamic&rdquo;</strong> JVM instruction, and becomes <strong>more modular</strong> than before. In this article, we&rsquo;re going to look into those new features in more detail.</p>
<h2 id="a-static-theme-for-a-dynamic-language">A &ldquo;static theme&rdquo; for a dynamic language</h2>
<h2 id="static-type-checking">Static type checking</h2>
<p>Groovy, by nature, is and will always be a dynamic language. However, Groovy is often used as a &ldquo;Java scripting language&rdquo;, or as a &ldquo;better Java&rdquo; (ie. a Java with less boilerplate and more power features).</p>
<p>A lot of Java developers actually use and embed Groovy in their Java applications as an extension language, to author more expressive business rules, to further customize the application for different customers, etc. For such Java-oriented use cases, developers don&rsquo;t need all the dynamic capabilities offered by the language, and they usually expect the same kind of feedback from the Groovy compiler as the one given by javac. In particular, they want to get compilation errors (rather than runtime errors) for things like typos on variable or method names, incorrect type assignments and the like. That&rsquo;s why Groovy 2 features <strong>static type checking support</strong>.</p>
<h2 id="spotting-obvious-typos">Spotting obvious typos</h2>
<p>The static type checker is built using Groovy&rsquo;s existing powerful AST (Abstract Syntax Tree) transformation mechanisms but for those not familiar with these mechanisms you can think of it as an optional compiler plugin triggered through an annotation. Being an optional feature, you are not forced to use it if you don&rsquo;t need it. To trigger static type checking, just use the <code>@TypeChecked</code> annotation on a method or on a class to turn on checking at your desired level of granularity. Let&rsquo;s see that in action with a first example:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.transform.TypeChecked</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">void</span> <span style="color:#06287e">someMethod</span><span style="color:#666">()</span> <span style="color:#666">{}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@TypeChecked</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">void</span> <span style="color:#06287e">test</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// compilation error:
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#60a0b0;font-style:italic">// cannot find matching method sommeeMethod()
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    sommeeMethod<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> name <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Marion&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// compilation error:
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#60a0b0;font-style:italic">// the variable naaammme is undeclared
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    println naaammme
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>We annotated the <code>test()</code> method with the <code>@TypeChecked</code> annotation, which instructs the Groovy compiler to run the static type checking for that particular method at compilation time. We&rsquo;re trying to call <code>someMethod()</code> with some obvious typos, and to print the name variable again with another typo, and the compiler will throw two compilation errors because respectively, the method and variable are not found or declared.</p>
<h2 id="check-your-assignments-and-return-values">Check your assignments and return values</h2>
<p>The static type checker also verifies that the return types and values of your assignments are coherent:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.transform.TypeChecked</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@TypeChecked</span>
</span></span><span style="display:flex;"><span>Date <span style="color:#06287e">test</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// compilation error:
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#60a0b0;font-style:italic">// cannot assign value of Date
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#60a0b0;font-style:italic">// to variable of type int
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#902000">int</span> object <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Date<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    String<span style="color:#666">[]</span> letters <span style="color:#666">=</span> <span style="color:#666">[</span><span style="color:#4070a0">&#39;a&#39;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#39;b&#39;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#39;c&#39;</span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// compilation error:
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#60a0b0;font-style:italic">// cannot assign value of type String
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#60a0b0;font-style:italic">// to variable of type Date
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    Date aDateVariable <span style="color:#666">=</span> letters<span style="color:#666">[</span><span style="color:#40a070">0</span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// compilation error:
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#60a0b0;font-style:italic">// cannot return value of type String
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#60a0b0;font-style:italic">// on method returning type Date
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#007020;font-weight:bold">return</span> <span style="color:#4070a0">&#34;today&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>In this example, the compiler will complain about the fact you cannot assign a <code>Date</code> in an <code>int</code> variable, nor can you return a <code>String</code> instead of a <code>Date</code> value specified in the method signature. The compilation error from the middle of the script is also interesting, as not only does it complain of the wrong assignment, but also because it shows type inference at play, because the type checker, of course, knows that <code>letters[0]</code> is of type <code>String</code>, because we&rsquo;re dealing with an array of <code>Strings</code>.</p>
<h2 id="more-on-type-inference">More on type inference</h2>
<p>Since we&rsquo;re mentioning type inference, let&rsquo;s have a look at some other occurrences of it. We mentioned the type checker tracks the return types and values:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.transform.TypeChecked</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@TypeChecked</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">int</span> <span style="color:#06287e">method</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span><span style="color:#007020;font-weight:bold">true</span><span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#60a0b0;font-style:italic">// compilation error:
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>        <span style="color:#60a0b0;font-style:italic">// cannot return value of type String
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>        <span style="color:#60a0b0;font-style:italic">// on method returning type int
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>        <span style="color:#4070a0">&#39;String&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">else</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#40a070">42</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Given a method returning a value of primitive type <code>int</code>, the type checker is able to also check the values returned from different constructs like <code>if / else </code>branches, <code>try / catch</code> blocks or <code>switch / case</code> blocks. Here, in our example, one branch of the <code>if / else</code> blocks tries to return a <code>String </code>value instead of a primitive <code>int</code>, and the compiler complains about it.</p>
<h2 id="common-type-conversions-still-allowed">Common type conversions still allowed</h2>
<p>The static type checker, however, won&rsquo;t complain for certain automatic type conversions that Groovy supports. For instance, for method signatures returning <code>String, boolean</code> or <code>Class</code>, Groovy converts return values to these types automatically:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.transform.TypeChecked</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@TypeChecked</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">boolean</span> <span style="color:#06287e">booleanMethod</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#34;non empty strings are evaluated to true&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">booleanMethod</span><span style="color:#666">()</span> <span style="color:#666">==</span> <span style="color:#007020;font-weight:bold">true</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@TypeChecked</span>
</span></span><span style="display:flex;"><span>String <span style="color:#06287e">stringMethod</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// StringBuilder converted to String calling toString()
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">StringBuilder</span><span style="color:#666">()</span> <span style="color:#666">&lt;&lt;</span> <span style="color:#4070a0">&#34;non empty string&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">stringMethod</span><span style="color:#666">()</span> <span style="color:#007020;font-weight:bold">instanceof</span> String
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@TypeChecked</span>
</span></span><span style="display:flex;"><span>Class <span style="color:#06287e">classMethod</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// the java.util.List class will be returned
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#4070a0">&#34;java.util.List&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">classMethod</span><span style="color:#666">()</span> <span style="color:#666">==</span> List
</span></span></code></pre></div><p>The static type checker is also clever enough to do <strong>type inference</strong>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.transform.TypeChecked</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@TypeChecked</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">void</span> <span style="color:#06287e">method</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> name <span style="color:#666">=</span> <span style="color:#4070a0">&#34; Guillaume &#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// String type inferred (even inside GString)
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    println <span style="color:#4070a0">&#34;NAME = ${name.toUpperCase()}&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// Groovy GDK method support
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#60a0b0;font-style:italic">// (GDK operator overloading too)
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    println name<span style="color:#666">.</span><span style="color:#4070a0">trim</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">int</span><span style="color:#666">[]</span> numbers <span style="color:#666">=</span> <span style="color:#666">[</span><span style="color:#40a070">1</span><span style="color:#666">,</span> <span style="color:#40a070">2</span><span style="color:#666">,</span> <span style="color:#40a070">3</span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// Element n is an int
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#007020;font-weight:bold">for</span> <span style="color:#666">(</span><span style="color:#902000">int</span> n <span style="color:#007020;font-weight:bold">in</span> numbers<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        println
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Although the <code>name</code> variable was defined with <code>def</code>, the type checker understands it is of type <code>String</code>. Then, when this variable is used in the interpolated string, it knows it can call <code>String's toUpperCase()</code> method, or the <code>trim()</code> method later one, which is a method added by the Groovy Development Kit decorating the <code>String </code>class. Last, when iterating over the elements of an array of primitive <code>ints</code>, it also understands that an element of that array is obviously an <code>int</code>.</p>
<h2 id="mixing-dynamic-features-and-statically-typed-methods">Mixing dynamic features and statically typed methods</h2>
<p>An important aspect to have in mind is that using the static type checking facility restricts what you are allowed to use in Groovy. Most runtime dynamic features are not allowed, as they can&rsquo;t be statically type checked at compilation time. So adding a new method at runtime through the type&rsquo;s metaclasses is not allowed. But when you need to use some particular dynamic feature, like Groovy&rsquo;s builders, you can opt out of static type checking should you wish to.</p>
<p>The <code>@TypeChecked</code> annotation can be put at the class level or at the method level. So if you want to have a whole class type checked, put the annotation on the class, and if you want only a few methods type checked, put the annotation on just those methods. Also, if you want to have everything type checked, except a specific method, you can annotate the latter with <code>@TypeChecked(TypeCheckingMode.SKIP) </code>- or <code>@TypeChecked(SKIP)</code> for short, if you statically import the associated enum. Let&rsquo;s illustrate the situation with the following script, where the <code>greeting()</code> method is type checked, whereas the <code>generateMarkup()</code> method is not:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.transform.TypeChecked</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.xml.MarkupBuilder</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// this method and its code are type checked
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#555;font-weight:bold">@TypeChecked</span>
</span></span><span style="display:flex;"><span>String <span style="color:#06287e">greeting</span><span style="color:#666">(</span>String name<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    generateMarkup<span style="color:#666">(</span>name<span style="color:#666">.</span><span style="color:#4070a0">toUpperCase</span><span style="color:#666">())</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// this method isn&#39;t type checked
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// and you can use dynamic features like the markup builder
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>String <span style="color:#06287e">generateMarkup</span><span style="color:#666">(</span>String name<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> sw <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> StringWriter<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">MarkupBuilder</span><span style="color:#666">(</span>sw<span style="color:#666">).</span><span style="color:#4070a0">html</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        body <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>            div name
</span></span><span style="display:flex;"><span>        <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    sw<span style="color:#666">.</span><span style="color:#4070a0">toString</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">greeting</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;Cédric&#34;</span><span style="color:#666">).</span><span style="color:#4070a0">contains</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;&lt;div&gt;CÉDRIC&lt;/div&gt;&#34;</span><span style="color:#666">)</span>
</span></span></code></pre></div><h2 id="type-inference-and-instanceof-checks">Type inference and instanceof checks</h2>
<p>Current production releases of Java don&rsquo;t support general type inference; hence we find today many places where code is often quite verbose and cluttered with boilerplate constructs. This obscures the intent of the code and without the support of powerful IDEs is also harder to write. This is the case with <code>instanceof </code>checks: You often check the class of a value with instanceof inside an <code>if</code> condition, and afterwards in the <code>if </code>block, you must still use casts to be able to use methods of the value at hand. In plain Groovy, as well as in the new static type checking mode, you can completely get rid of those casts.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.transform.TypeChecked</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.xml.MarkupBuilder</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@TypeChecked</span>
</span></span><span style="display:flex;"><span>String <span style="color:#06287e">test</span><span style="color:#666">(</span>Object val<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>val <span style="color:#007020;font-weight:bold">instanceof</span> String<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#60a0b0;font-style:italic">// unlike Java:
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>        <span style="color:#60a0b0;font-style:italic">// return ((String)val).toUpperCase()
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>        val<span style="color:#666">.</span><span style="color:#4070a0">toUpperCase</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">else</span> <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>val <span style="color:#007020;font-weight:bold">instanceof</span> Number<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#60a0b0;font-style:italic">// unlike Java:
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>        <span style="color:#60a0b0;font-style:italic">// return ((Number)val).intValue().multiply(2)
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>        val<span style="color:#666">.</span><span style="color:#4070a0">intValue</span><span style="color:#666">()</span> <span style="color:#666">*</span> <span style="color:#40a070">2</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">test</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;abc&#39;</span><span style="color:#666">)</span> <span style="color:#666">==</span> <span style="color:#4070a0">&#39;ABC&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">test</span><span style="color:#666">(</span><span style="color:#40a070">123</span><span style="color:#666">)</span> <span style="color:#666">==</span> <span style="color:#4070a0">&#39;246&#39;</span>
</span></span></code></pre></div><p>In the above example, the static type checker knows that the val parameter is of type <code>String</code> inside the <code>if </code>block, and of type<code> Number</code> in the else if block, without requiring any cast.</p>
<h2 id="lowest-upper-bound">Lowest Upper Bound</h2>
<p>The static type checker goes a bit further in terms of type inference in the sense that it has a more granular understanding of the type of your objects. Consider the following code:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.transform.TypeChecked</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// inferred return type:
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// a list of numbers which are comparable and serializable
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#555;font-weight:bold">@TypeChecked</span> test<span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// an integer and a BigDecimal
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#007020;font-weight:bold">return</span> <span style="color:#666">[</span><span style="color:#40a070">1234</span><span style="color:#666">,</span> <span style="color:#40a070">3.14</span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>In this example, we return, intuitively, a list of numbers: an <code>Integer</code> and a <code>BigDecimal</code>. But the static type checker computes what we call a <em>&ldquo;lowest upper bound&rdquo;</em>, which is actually a list of numbers which are also serializable and comparable. It&rsquo;s not possible to denote that type with the standard Java type notation, but if we had some kind of intersection operator like an ampersand, it could look like <code>List&lt;Number &amp; Serializable &amp; Comparable&gt;.</code></p>
<h2 id="flow-typing">Flow typing</h2>
<p>Although this is not really recommended as a good practice, sometimes developers use the same untyped variable to store values of different types. Look at this method body:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.transform.TypeChecked</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@TypeChecked</span> test<span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> <span style="color:#902000">var</span> <span style="color:#666">=</span> <span style="color:#40a070">123</span>             <span style="color:#60a0b0;font-style:italic">// inferred type is int
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#902000">var</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#34;123&#34;</span>               <span style="color:#60a0b0;font-style:italic">// assign var with a String
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>
</span></span><span style="display:flex;"><span>    println <span style="color:#902000">var</span><span style="color:#666">.</span><span style="color:#4070a0">toInteger</span><span style="color:#666">()</span>   <span style="color:#60a0b0;font-style:italic">// no problem, no need to cast
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">var</span> <span style="color:#666">=</span> <span style="color:#40a070">123</span>
</span></span><span style="display:flex;"><span>    println <span style="color:#902000">var</span><span style="color:#666">.</span><span style="color:#4070a0">toUpperCase</span><span style="color:#666">()</span> <span style="color:#60a0b0;font-style:italic">// error, var is int!
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#666">}</span>
</span></span></code></pre></div><p>The <code>var</code> variable is initialized with an <code>int</code>. Then, a <code>String</code> is assigned. The <em>&ldquo;flow typing&rdquo;</em> algorithm follows the flow of assignment and understands that the variable now holds a<code> String</code>, so the static type checker will be happy with the <code>toInteger()</code> method added by Groovy on top of <code>String</code>. Next, a number is put back in the var variable, but then, when calling <code>toUpperCase()</code>, the type checker will throw a compilation error, as there&rsquo;s no <code>toUpperCase()</code> method on <code>Integer</code>.</p>
<p>There are some special cases for the flow typing algorithm when a variable is shared with a closure which are interesting. What happens when a local variable is referenced in a closure inside a method where that variable is defined? Let&rsquo;s have a look at this example:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.transform.TypeChecked</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@TypeChecked</span> test<span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> <span style="color:#902000">var</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#34;abc&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> cl <span style="color:#666">=</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span><span style="color:#007020;font-weight:bold">new</span> Random<span style="color:#666">().</span><span style="color:#4070a0">nextBoolean</span><span style="color:#666">())</span> <span style="color:#902000">var</span> <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Date<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    cl<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">var</span><span style="color:#666">.</span><span style="color:#4070a0">toUpperCase</span><span style="color:#666">()</span> <span style="color:#60a0b0;font-style:italic">// compilation error!
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#666">}</span>
</span></span></code></pre></div><p>The <code>var</code> local variable is assigned a <code>String</code>, but then, <code>var</code> might be assigned a <code>Date</code> if some random value is true. Typically, it&rsquo;s only at runtime that we really know if the condition in the if statement of the closure is made or not. Hence, at compile-time, there&rsquo;s no chance the compiler can know if <code>var</code> now contains a <code>String</code> or a <code>Date</code>. That&rsquo;s why the compiler will actually complain about the <code>toUpperCase()</code> call, as it is not able to infer that the variable contains a <code>String</code> or not. This example is certainly a bit contrived, but there are some more interesting cases:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.transform.TypeChecked</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">A</span>           <span style="color:#666">{</span> <span style="color:#902000">void</span> foo<span style="color:#666">()</span> <span style="color:#666">{}</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">B</span> <span style="color:#007020;font-weight:bold">extends</span> A <span style="color:#666">{</span> <span style="color:#902000">void</span> bar<span style="color:#666">()</span> <span style="color:#666">{}</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@TypeChecked</span> test<span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> <span style="color:#902000">var</span> <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> A<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> cl <span style="color:#666">=</span> <span style="color:#666">{</span> <span style="color:#902000">var</span> <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> B<span style="color:#666">()</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    cl<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// var is at least an instance of A
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#60a0b0;font-style:italic">// so we are allowed to call method foo()
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#902000">var</span><span style="color:#666">.</span><span style="color:#4070a0">foo</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>In the <code>test()</code> method above, <code>var</code> is assigned an instance of<code> A</code>, and then an instance of <code>B</code> in the closure which is call afterwards, so we can at least infer that var is of type <code>A</code>.</p>
<p>All those checks added to the Groovy compiler are done at compile-time, but the generated bytecode is still the same dynamic code as usual - no changes in behavior at all.</p>
<p>Since the compiler now knows a lot more about your program in terms of types, it opens up some interesting possibilities: what about compiling that type checked code statically? The obvious advantage will be that the generated bytecode will more closely resemble the bytecode created by the javac compiler itself, making statically compiled Groovy code as fast as plain Java, among other advantages. In the next section, we&rsquo;ll learn more about Groovy&rsquo;s static compilation.</p>
<h2 id="static-compilation">Static compilation</h2>
<p>As we shall see in the following chapter about the JDK 7 alignments, Groovy 2.0 supports the new <em>&ldquo;invoke dynamic&rdquo;</em> instruction of the JVM and its related APIs, facilitating the development of dynamic languages on the Java platform and bringing some additional performance to Groovy&rsquo;s dynamic calls. However, unfortunately shall I say, JDK 7 is not widely deployed in production at the time of this writing, so not everybody has the chance to run on the latest version. So developers looking for performance improvements would not see much changes in Groovy 2.0, if they aren&rsquo;t able to run on JDK 7. Luckily, the Groovy development team thought those developers could get interesting performance boost, among other advantages, by allowing type checked code to be compiled statically.</p>
<p>Without further ado, let&rsquo;s dive in and use the new <code>@CompileStatic</code> transform:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.transform.CompileStatic</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@CompileStatic</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">int</span> <span style="color:#06287e">squarePlusOne</span><span style="color:#666">(</span><span style="color:#902000">int</span> num<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    num <span style="color:#666">*</span> num <span style="color:#666">+</span> <span style="color:#40a070">1</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">squarePlusOne</span><span style="color:#666">(</span><span style="color:#40a070">3</span><span style="color:#666">)</span> <span style="color:#666">==</span> <span style="color:#40a070">10</span>
</span></span></code></pre></div><p>This time, instead of using <code>@TypeChecked</code>, use <code>@CompileStatic</code>, and your code will be statically compiled, and the bytecode generated here will look like javac&rsquo;s bytecode, running just as fast. Like the <code>@TypeChecked annotation, @CompileStatic </code>can annotate classes and methods, and <code>@CompileStatic(SKIP) </code>can bypass static compilation for a specific method, when its class is marked with <code>@CompileStatic</code>.</p>
<p>Another advantage of the javac-like bytecode generation is that the size of the bytecode for those annotated methods will be smaller than the usual bytecode generated by Groovy for dynamic methods, since to support Groovy&rsquo;s dynamic features, the bytecode in the dynamic case contains additional instructions to call into Groovy&rsquo;s runtime system.</p>
<p>Last but not least, static compilation can be used by framework or library code writers to help avoid adverse interactions when dynamic metaprogramming is in use in several parts of the codebase. The dynamic features available in languages like Groovy are what give developers incredible power and flexibility but if care is not taken, different assumptions can exist in different parts of the system with regards to what metaprogramming features are in play and this can have unintended consequences. As a slightly contrived example, consider what happens if you are using two different libraries, both of which add a similarly named but differently implemented method to one of your core classes. What behaviour is expected? Experienced users of dynamic languages will have seen this problem before and probably heard it referred to as <em>&ldquo;monkey patching&rdquo;</em>. Being able to statically compile parts of your code base - those parts that don&rsquo;t need dynamic features - shields you from the effects of monkey patching, as the statically compiled code doesn&rsquo;t go through Groovy&rsquo;s dynamic runtime system. Although dynamic runtime aspects of the language are not allowed in a static compilation context, all the usual AST transformation mechanisms work just as well as before, since most AST transforms perform their magic at compilation time.</p>
<p>In terms of performance, Groovy&rsquo;s statically compiled code is usually more or less as fast as javac&rsquo;s. In the few micro-benchmarks the development team used, performance is identical in several cases, and sometimes it&rsquo;s slightly slower.</p>
<p>Historically, thanks to the transparent and seamless integration of Java and Groovy, we used to advise developers to optimize some hotspot routines in Java for further performance gains, but now, with this static compilation option, this is no longer the case, and people wishing to develop their projects in full Groovy can do so.</p>
<h2 id="the-java-7-and-jdk-7-theme">The Java 7 and JDK 7 theme</h2>
<p>The grammar of the Groovy programming language actually derives from the Java grammar itself, but obviously, Groovy provides additional nice shortcuts to make developers more productive. This familiarity of syntax for Java developers has always been a key selling point for the project and its wide adoption, thanks to a flat learning curve. And of course, we expect Groovy users and newcomers to also want to benefit from the few syntax refinements offered by Java 7 with its <em>&ldquo;Project Coin&rdquo;</em> additions.</p>
<p>Beyond the syntax aspects, JDK 7 also brings interesting novelties to its APIs, and for a first time in a long time, even a new bytecode instruction called <em>&ldquo;invoke dynamic&rdquo;</em>, which is geared towards helping implementors develop their dynamic languages more easily and benefit from more performance.</p>
<h2 id="project-coin-syntax-enhancements">Project Coin syntax enhancements</h2>
<p>Since day 1 (that was back in 2003 already!) Groovy has had several syntax enhancements and features on top of Java. One can think of closures, for example, but also the ability to put more than just discrete values in <code>switch / case</code> statements, where Java 7 only allows <code>Strings </code>in addition. So some of the Project Coin syntax enhancements, like <code>Strings </code>in switch, were already present in Groovy. However, some of the enhancements are new, such as binary literals, underscore in number literals, or the multi catch block, and Groovy 2 supports them. The sole omission from the Project Coin enhancements is the &ldquo;try with resources&rdquo; construct, for which Groovy already provides various alternatives through the rich API of the Groovy Development Kit.</p>
<h2 id="binary-literals">Binary literals</h2>
<p>In Java 6 and before, as well as in Groovy, numbers could be represented in decimal, octal and hexadecimal bases, and with Java 7 and Groovy 2, you can use a binary notation with the &ldquo;0b&rdquo; prefix:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">int</span> x <span style="color:#666">=</span> <span style="color:#40a070">0</span>b10101111
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> x <span style="color:#666">==</span> <span style="color:#40a070">175</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">byte</span> aByte <span style="color:#666">=</span> <span style="color:#40a070">0</span>b00100001
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> aByte <span style="color:#666">==</span> <span style="color:#40a070">33</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">int</span> anInt <span style="color:#666">=</span> <span style="color:#40a070">0</span>b1010000101000101
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> anInt <span style="color:#666">==</span> <span style="color:#40a070">41285</span>
</span></span></code></pre></div><h2 id="underscore-in-number-literals">Underscore in number literals</h2>
<p>When writing long literal numbers, it&rsquo;s harder on the eye to figure out how some numbers are grouped together, for example with groups of thousands, of words, etc. By allowing you to place underscore in number literals, it&rsquo;s easier to spot those groups:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">long</span> creditCardNumber <span style="color:#666">=</span> <span style="color:#40a070">1234</span>_5678_9012_3456L
</span></span><span style="display:flex;"><span><span style="color:#902000">long</span> socialSecurityNumbers <span style="color:#666">=</span> <span style="color:#40a070">999</span>_99_9999L
</span></span><span style="display:flex;"><span><span style="color:#902000">double</span> monetaryAmount <span style="color:#666">=</span> <span style="color:#40a070">12</span>_345_132<span style="color:#666">.</span><span style="color:#40a070">12</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">long</span> hexBytes <span style="color:#666">=</span> <span style="color:#40a070">0xFF</span>_EC_DE_5E
</span></span><span style="display:flex;"><span><span style="color:#902000">long</span> hexWords <span style="color:#666">=</span> <span style="color:#40a070">0xFFEC</span>_DE5E
</span></span><span style="display:flex;"><span><span style="color:#902000">long</span> maxLong <span style="color:#666">=</span> <span style="color:#40a070">0x7fff</span>_ffff_ffff_ffffL
</span></span><span style="display:flex;"><span><span style="color:#902000">long</span> alsoMaxLong <span style="color:#666">=</span> <span style="color:#40a070">9</span>_223_372_036_854_775_807L
</span></span><span style="display:flex;"><span><span style="color:#902000">long</span> bytes <span style="color:#666">=</span> <span style="color:#40a070">0</span>b11010010_01101001_10010100_10010010
</span></span></code></pre></div><h2 id="multicatch-block">Multicatch block</h2>
<p>When catching exceptions, we often replicate the catch block for two or more exceptions as we want to handle them in the same way. A workaround is either to factor out the commonalities in its own method, or in a more ugly fashion to have a catch-all approach by catching <code>Exception</code>, or worse, <code>Throwable</code>. With the multi catch block, we&rsquo;re able to define several exceptions to be catch and treated by the same catch block:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">/* ... */</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">catch</span><span style="color:#666">(</span>IOException <span style="color:#666">|</span> NullPointerException e<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">/* one block to handle 2 exceptions */</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><h2 id="invoke-dynamic-support">Invoke Dynamic support</h2>
<p>As we mentioned earlier in this article, JDK 7 came with a new bytecode instruction called <em>&ldquo;invoke dynamic&rdquo;</em>, as well as with its associated APIs. Their goal is to help dynamic language implementors in their job of crafting their languages on top of the Java platform, by simplifying the wiring of dynamic method calls, by defining <em>&ldquo;call sites&rdquo;</em> where dynamic method call section can be cached, <em>&ldquo;method handles&rdquo;</em> as method pointers, <em>&ldquo;class values&rdquo;</em> to store any kind of metadata along class objects, and a few other things. One caveat though, despite promising performance improvements, &ldquo;invoke dynamic&rdquo; hasn&rsquo;t yet fully been optimized inside the JVM, and doesn&rsquo;t yet always deliver the best performance possible, but update after update, the optimizations are coming.</p>
<p>Groovy brought its own implementation techniques, to speed up method selection and invocation with &ldquo;call site caching&rdquo;, to store metaclasses (the dynamic runtime equivalent of classes) with its metaclass registry, to perform native primitive calculations as fast as Java, and much more. But with the advent of &ldquo;invoke dynamic&rdquo;, we can rebase the implementation of Groovy on top of these APIs and this JVM bytecode instruction, to gain performance improvements and to simplify our code base.</p>
<p>If you&rsquo;re lucky to run on JDK 7, you&rsquo;ll be able to use a new version of the Groovy JARs which has been compiled with the &ldquo;invoke dynamic&rdquo; support. Those JARs are easily recognizable as they use the &ldquo;-indy&rdquo; classifier in their names.</p>
<h2 id="enabling-invoke-dynamic-support">Enabling invoke dynamic support</h2>
<p>Using the &ldquo;indy&rdquo; JARs is not enough, however, to compile your Groovy code so that it leverages the &ldquo;invoke dynamic&rdquo; support. For that, you&rsquo;ll have to use the &ndash;indy flag when using the &ldquo;groovyc&rdquo; compiler or the &ldquo;groovy&rdquo; command. This also means that even if you&rsquo;re using the indy JARs, you can still target JDK 5 or 6 for compilation.</p>
<p>Similarly, if you&rsquo;re using the groovyc Ant task for compiling your projects, you can also specify the indy attribute:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;taskdef</span> <span style="color:#4070a0">name=</span><span style="color:#4070a0">&#34;groovyc&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">classname=</span><span style="color:#4070a0">&#34;org.codehaus.groovy.ant.Groovyc&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">classpathref=</span><span style="color:#4070a0">&#34;cp&#34;</span><span style="color:#062873;font-weight:bold">/&gt;</span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;groovyc</span> <span style="color:#4070a0">srcdir=</span><span style="color:#4070a0">&#34;${srcDir}&#34;</span> <span style="color:#4070a0">destdir=</span><span style="color:#4070a0">&#34;${destDir}&#34;</span> <span style="color:#4070a0">indy=</span><span style="color:#4070a0">&#34;true&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;classpath&gt;</span>
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;/classpath&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/groovyc&gt;</span>
</span></span></code></pre></div><p>The Groovy Eclipse Maven compiler plugin hasn&rsquo;t yet been updated with the support of Groovy 2.0 but this will be the case shortly. For GMaven plugin users, although it&rsquo;s possible to configure the plugin to use Groovy 2.0 already, there&rsquo;s currently no flag to enable the invoke dynamic support. Again, GMaven will also be updated soon in that regard.</p>
<p>When integrating Groovy in your Java applications, with <code>GroovyShell</code>, for example, you can also enable the invoke dynamic support by passing a <code>CompilerConfiguration</code> instance to the <code>GroovyShell</code> constructor on which you access and set the optimization options:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>CompilerConfiguration<span style="color:#bbb"> </span>config<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>CompilerConfiguration();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>config.<span style="color:#4070a0">getOptimizationOptions</span>().<span style="color:#4070a0">put</span>(<span style="color:#666">**</span><span style="color:#4070a0">&#34;**indy**&#34;</span><span style="color:#666">**</span>,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">true</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>config.<span style="color:#4070a0">getOptimizationOptions</span>().<span style="color:#4070a0">put</span>(<span style="color:#666">**</span><span style="color:#4070a0">&#34;int&#34;</span><span style="color:#666">**</span>,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">false</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>GroovyShell<span style="color:#bbb"> </span>shell<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>GroovyShell(config);<span style="color:#bbb">
</span></span></span></code></pre></div><p>As invokedynamic is supposed to be a full replacement to dynamic method dispatch, it is also necessary to disable the primitive optimizations which generate extra bytecode that is here to optimize edge cases. Even if it is in some cases slower than with primitive optimizations activated, future versions of the JVM will feature an improved JIT which will be capable of inlining most of the calls and remove unnecessary boxings.</p>
<h2 id="promising-performance-improvements">Promising performance improvements</h2>
<p>In our testing, we noticed some interesting performance gains in some areas, whereas other programs could run slower than when not using the invoke dynamic support. The Groovy team has further performance improvements in the pipeline for Groovy 2.1 however, but we noticed the JVM isn&rsquo;t yet finely tuned and still has a long way to go to be fully optimized. But fortunately, upcoming JDK 7 updates (in particular update 8) should already contain such improvements, so the situation can only improve. Furthermore, as invoke dynamic is used for the implementation of JDK 8 Lambdas, we can be sure more improvements are forthcoming.</p>
<h2 id="a-more-modular-groovy">A more modular Groovy</h2>
<p>We&rsquo;ll finish our journey through the new features of Groovy 2.0 by speaking about modularity. Just like Java, Groovy is not just a language, but it&rsquo;s also a set of APIs serving various purposes: templating, Swing UI building, Ant scripting, JMX integration, SQL access, servlet serving, and more. The Groovy deliverables were bundling all these features and APIs inside a single big JAR. However, not everybody needs everything at all times in their own applications: you might be interested in the template engine and the servlets if you&rsquo;re writing some web application, but you might only need the Swing builder when working on a rich desktop client program.</p>
<h2 id="groovy-modules">Groovy modules</h2>
<p>So the first goal of the modularity aspect of this release is to actually split the original Groovy JAR into smaller modules, smaller JARs. The core Groovy JAR is now twice as small, and we have the following feature modules available:</p>
<ul>
<li><strong>Ant</strong>: for scripting Ant tasks for automating administration tasks</li>
<li><strong>BSF</strong>: for integrating Groovy in your Java applications with the old Apache Bean Scripting Framework</li>
<li><strong>Console</strong>: module containing the Groovy Swing console</li>
<li><strong>GroovyDoc</strong>: for documenting your Groovy and Java classes</li>
<li><strong>Groovysh</strong>: module corresponding to the Groovysh command-line shell</li>
<li><strong>JMX</strong>: for exposing and consuming JMX beans</li>
<li><strong>JSON</strong>: for producing and consuming JSON payloads</li>
<li><strong>JSR-223</strong>: for integrating Groovy in your Java applications with the JDK 6+ javax.scripting APIs</li>
<li><strong>Servlet</strong>: for writing and serving Groovy script servlets and templates</li>
<li><strong>SQL</strong>: for querying relational databases</li>
<li><strong>Swing</strong>: for building Swing UIs</li>
<li><strong>Templates</strong>: for using the template engine</li>
<li><strong>Test</strong>: for some test support, like the GroovyTestCase, mocking, and more</li>
<li><strong>TestNG</strong>: for writing TestNG tests in Groovy</li>
<li><strong>XML</strong>: for producing and consuming XML documents</li>
</ul>
<p>With Groovy 2, you&rsquo;re now able to just pick up the modules you&rsquo;re interested in, rather than bringing everything on your classpath. However, we still provide the &ldquo;all&rdquo; JAR which contains everything, if you don&rsquo;t want to complicate your dependencies for just a few megabytes of saved space. We also provide those JARs compiled with the &ldquo;invoke dynamic&rdquo; support as well, for those running on JDK 7.</p>
<h2 id="extension-modules">Extension modules</h2>
<p>The work on making Groovy more modular also yielded an interesting new feature: extension modules. By splitting Groovy into smaller modules, a mechanism for modules to contribute extension methods has been created. That way, extension modules can provide instance and static methods to other classes, including the ones from the JDK or third-party libraries. Groovy uses this mechanism to decorate classes from the JDK, to add new useful methods to classes like <code>String, File</code>, streams, and many more - for example, a <code>getText()</code> method on URL allows you to retrieve the content of a remote URL through an HTTP get. Notice also that those extension methods in your modules are also understood by the static type checker and compiler. But let&rsquo;s now have a look at how you can add new methods to existing types.</p>
<h2 id="contributing-an-instance-method">Contributing an instance method</h2>
<p>To add new methods to an existing type, you&rsquo;ll have to create a helper class that will contain those methods. Inside that helper class, all the extension methods will actually be <code>public</code> (the default for Groovy but required if implementing in Java) and <code>static</code> (although they will be available on instances of that class). They will always take a first parameter which is actually the instance on which this method will be called. And then following parameters will be the parameters passed when calling the method. This is the same convention use for Groovy categories.</p>
<p>Say we want to add a <code>greets()</code> method on <code>String,</code> that would greet the name of the person passed in parameters, so that you could that method as follow:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#4070a0">&#34;Guillaume&#34;</span><span style="color:#666">.</span><span style="color:#4070a0">greets</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;Paul&#34;</span><span style="color:#666">)</span> <span style="color:#666">==</span> <span style="color:#4070a0">&#34;Hi Paul, I&#39;m Guillaume&#34;</span>
</span></span></code></pre></div><p>To accomplish that, you will create a helper class with an extension method like so:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">package</span> com<span style="color:#666">.</span><span style="color:#4070a0">acme</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">MyExtension</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">static</span> String <span style="color:#06287e">greets</span><span style="color:#666">(</span>String self<span style="color:#666">,</span> String name<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#4070a0">&#34;Hi ${name}, I&#39;m ${self}&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><h2 id="contributing-a-static-method">Contributing a static method</h2>
<p>For static extension methods, this is the same mechanism and convention. Let&rsquo;s add a new static method to Random to get a random integer between two values, you could proceed as in this class:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">package</span> com<span style="color:#666">.</span><span style="color:#4070a0">acme</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">MyStaticExtension</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">static</span> String <span style="color:#06287e">between</span><span style="color:#666">(</span>Random selfType<span style="color:#666">,</span> <span style="color:#902000">int</span> start<span style="color:#666">,</span> <span style="color:#902000">int</span> end<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">Random</span><span style="color:#666">().</span><span style="color:#4070a0">nextInt</span><span style="color:#666">(</span>end <span style="color:#666">-</span> start <span style="color:#666">+</span> <span style="color:#40a070">1</span><span style="color:#666">)</span> <span style="color:#666">+</span> start
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>That way, you are able to use that extension method as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>Random<span style="color:#666">.</span><span style="color:#4070a0">between</span><span style="color:#666">(</span><span style="color:#40a070">3</span><span style="color:#666">,</span> <span style="color:#40a070">4</span><span style="color:#666">)</span>
</span></span></code></pre></div><h2 id="extension-module-descriptor">Extension module descriptor</h2>
<p>Once you&rsquo;ve coded your helper classes (in Groovy or even in Java) containing the extension methods, you need to create a descriptor for your module. You must create a file called <code>org.codehaus.groovy.runtime.ExtensionModule</code> in the <code>META-INF/services</code> directory of your module archive. Four essential fields can be defined, to tell the Groovy runtime about the name and version of your module, as well as to point at your helper classes for extension methods with a comma-separated list of class names. Here is what our final module descriptor looks like:</p>
<pre tabindex="0"><code>moduleName = MyExtension
moduleVersion = 1.0
extensionClasses = com.acme.MyExtension
staticExtensionClasses = com.acme.MyStaticExtension
</code></pre><p>With this extension module descriptor on the classpath, you are now able to use those extension methods in your code, without needing an import or anything else, as those extension methods are automatically registered.</p>
<h2 id="grabbing-an-extension">Grabbing an extension</h2>
<p>With the @Grab annotation in your scripts, you can fetch dependencies from Maven repositories like Maven Central. With the addition of the @GrabResolver annotation, you can specify your own location for your dependencies as well. If you are &ldquo;grabbing&rdquo; an extension module dependency through this mechanism, the extension method will also be installed automatically. Ideally, for consistency, your module name and version should be coherent with the artifact id and version of your artifact.</p>
<h2 id="summary">Summary</h2>
<p>Groovy is very popular among Java developers and offers them a mature platform and ecosystem for their application needs. But without resting still, the Groovy development team continues to further improve the language and its APIs to help its users increase their productivity on the Java platform.</p>
<p>Groovy 2.0 responds to three key themes:</p>
<ul>
<li><strong>More performance</strong>: with the <strong>support of JDK 7 Invoke Dynamic</strong> to speed up Groovy for those lucky to have JDK 7 already in production, but also with <strong>static compilation</strong> for JDK 5 and beyond for everyone, and particularly those ready to abandon some aspects of dynamicity to shield themselves from the reach of &ldquo;monkey patching&rdquo; and to gain the <strong>same speed as Java</strong>.</li>
<li><strong>More Java friendliness</strong>: with the support of the <strong>Java 7 Project Coin enhancements</strong> to keep Groovy and Java as close syntax cousins as ever, and with the <strong>static type checker</strong> to have the same level of feedback and type safety as provided by the javac compiler for developers using Groovy as a Java scripting language</li>
<li><strong>More modularity</strong>: with a new level of modularity, Groovy opens the doors for <strong>smaller deliverables</strong>, for example for integration in <strong>mobile applications</strong> on Android, and allowing the Groovy APIs to grow and evolve with newer versions and newer extension modules, as well as allowing users to contribute extension methods to existing types.</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.0 released!</title><link>https://glaforge.dev/posts/2012/06/28/groovy-2-0-released/</link><pubDate>Thu, 28 Jun 2012 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/06/28/groovy-2-0-released/</guid><description>&lt;p>The Groovy development team is very pleased to announce the release of Groovy 2.0, the highly popular dynamic language for the Java platform.&lt;/p>
&lt;p>For the key highlights of this important milestone, Groovy 2.0 comes packed with:&lt;/p>
&lt;ul>
&lt;li>a static type checker to let the compiler tell you about the correctness of your code,&lt;/li>
&lt;li>static compilation for the performance of the critical parts of your application,&lt;/li>
&lt;li>modularity, splitting the Groovy JAR into smaller feature-oriented JARs and letting you create your own extension modules,&lt;/li>
&lt;li>JDK 7 Project Coin syntax enhancements, so that Groovy is still as friendly as possible with its Java cousin,&lt;/li>
&lt;li>and JDK 7 Invoke Dynamic integration to benefit from the support of the JVM for dynamic languages.&lt;/li>
&lt;/ul>
&lt;p>To learn more about all those great key new features, please &lt;a href="http://bit.ly/groovy2">read the Groovy 2.0 article on InfoQ&lt;/a> that I’ve written, detailing and explaining all those novelties.&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy development team is very pleased to announce the release of Groovy 2.0, the highly popular dynamic language for the Java platform.</p>
<p>For the key highlights of this important milestone, Groovy 2.0 comes packed with:</p>
<ul>
<li>a static type checker to let the compiler tell you about the correctness of your code,</li>
<li>static compilation for the performance of the critical parts of your application,</li>
<li>modularity, splitting the Groovy JAR into smaller feature-oriented JARs and letting you create your own extension modules,</li>
<li>JDK 7 Project Coin syntax enhancements, so that Groovy is still as friendly as possible with its Java cousin,</li>
<li>and JDK 7 Invoke Dynamic integration to benefit from the support of the JVM for dynamic languages.</li>
</ul>
<p>To learn more about all those great key new features, please <a href="http://bit.ly/groovy2">read the Groovy 2.0 article on InfoQ</a> that I’ve written, detailing and explaining all those novelties.</p>
<p>To download Groovy 2.0, go to the <a href="http://groovy.codehaus.org/Download?nc=1">download area</a> of the Groovy website.</p>
<p>For further information on all the JIRA issues fixed in this release and the various betas and release candidates, you can have a look at the <a href="http://bit.ly/groovy2jira">JIRA changelog</a>.</p>
<p>We’d like to thank all our users, all the contributors and committers who made this important release a reality, thanks to their feedback, their support, and their contributions.</p>
<p>Have a Groovy day!</p>
<p>Guillaume Laforge, on the behalf of the Groovy team.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A bit of functional tests and concurrency for Gaelyk</title><link>https://glaforge.dev/posts/2012/06/01/a-bit-of-functional-tests-and-concurrency-for-gaelyk/</link><pubDate>Fri, 01 Jun 2012 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/06/01/a-bit-of-functional-tests-and-concurrency-for-gaelyk/</guid><description>&lt;p>Along with the &lt;a href="https://glaforge.dev/posts/2012/05/30/gaelyk-1-2-is-out/">release of Gaelyk 1.2&lt;/a>, I&amp;rsquo;d like to share two interesting links about running functional tests with Geb, and concurrency / parallelism with GPars on &lt;a href="http://gaelyk.appspot.com">Gaelyk&lt;/a>.&lt;br />
Gaelyk functional testing with Geb&lt;/p>
&lt;p>In the Groovy ecosystem, we&amp;rsquo;re all aware of the &lt;a href="http://code.google.com/p/spock/">Spock&lt;/a> testing framework. On top of Spock, you can use the &lt;a href="http://www.gebish.org/">Geb&lt;/a> browser automation library, to easily create functional tests for your web applications, in a nice, readable and expressive fashion.&lt;/p></description><content:encoded>
<![CDATA[<p>Along with the <a href="https://glaforge.dev/posts/2012/05/30/gaelyk-1-2-is-out/">release of Gaelyk 1.2</a>, I&rsquo;d like to share two interesting links about running functional tests with Geb, and concurrency / parallelism with GPars on <a href="http://gaelyk.appspot.com">Gaelyk</a>.<br />
Gaelyk functional testing with Geb</p>
<p>In the Groovy ecosystem, we&rsquo;re all aware of the <a href="http://code.google.com/p/spock/">Spock</a> testing framework. On top of Spock, you can use the <a href="http://www.gebish.org/">Geb</a> browser automation library, to easily create functional tests for your web applications, in a nice, readable and expressive fashion.</p>
<p>Thanks to <a href="http://blog.proxerd.pl/">Marcin Erdmann</a>, the <a href="https://github.com/bmuschko/gradle-gae-plugin">Gradle GAE plugin</a> (used by the <a href="http://gaelyk.appspot.com/tutorial/template-project">Gaelyk template project</a>) has been enhanced with support for running functional tests with Spock and Geb. Marcin (who&rsquo;s blog is actually a fork of my <a href="https://github.com/glaforge/bloogaey">Bloogaey</a> app) has written a <a href="http://blog.proxerd.pl/article/funcational-testing-of-gae-lyk-applications-with-geb">small tutorial on how to get started</a>.</p>
<h2 id="concurrency-and-parallelism-with-gpars">Concurrency and parallelism with GPars</h2>
<p>Another great library of the Groovy universe is <a href="http://gpars.codehaus.org/">GPars</a>, which is bundled with the Groovy distribution. GPars offers a ton of features for your concurrency and parallelism needs: actors, fork/join abstractions, dataflow concurrency, agents, etc.</p>
<p>On Google App Engine, until recently, it wasn&rsquo;t allowed to spawn threads in your applications. So it was impossible to use a library like GPars in your Gaelyk applications. Fortunately, Google introduced a <a href="https://developers.google.com/appengine/docs/java/javadoc/com/google/appengine/api/ThreadManager">ThreadManager class</a> so that you can spawn up to 10 threads in your incoming requests.</p>
<p>Gaelyk committer <a href="https://twitter.com/musketyr">Vladimír Oraný</a> teamed up with Vacláv Pech (lead of GPars) to work on an extension library to allow GPars to use that thread manager for GPars&rsquo; own thread pools. Vacláv tells us about this story on <a href="http://www.jroller.com/vaclav/entry/gpars_actors_and_dataflow_for">his blog</a> and even points at the <a href="http://gparsconsole.appspot.com/">GPars web console</a> where you can try some parallel / concurrent features from GPars.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Release candidate of Groovy 2.0 available</title><link>https://glaforge.dev/posts/2012/05/31/release-candidate-of-groovy-2-0-available/</link><pubDate>Thu, 31 May 2012 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/05/31/release-candidate-of-groovy-2-0-available/</guid><description>&lt;p>This is with great pleasure that the development team announces the release candidate of Groovy 2.0.&lt;/p>
&lt;p>For the impatient among you, you can download Groovy 2.0 RC-1 in the &lt;a href="http://groovy.codehaus.org/Download?nc">download area&lt;/a> of the Groovy website.
And read the &lt;a href="https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;version=18472">JIRA changelog&lt;/a>.&lt;/p>
&lt;p>The big change in this release candidate is the modularity aspect. We&amp;rsquo;ve now fully switched to Gradle, as our build tool, to build a more modular Groovy. You still have a big &amp;ldquo;all&amp;rdquo; JAR, but if you&amp;rsquo;re interested in just picking the core JAR and the needed modules for your project, you can now do so.&lt;/p></description><content:encoded>
<![CDATA[<p>This is with great pleasure that the development team announces the release candidate of Groovy 2.0.</p>
<p>For the impatient among you, you can download Groovy 2.0 RC-1 in the <a href="http://groovy.codehaus.org/Download?nc">download area</a> of the Groovy website.
And read the <a href="https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=18472">JIRA changelog</a>.</p>
<p>The big change in this release candidate is the modularity aspect. We&rsquo;ve now fully switched to Gradle, as our build tool, to build a more modular Groovy. You still have a big &ldquo;all&rdquo; JAR, but if you&rsquo;re interested in just picking the core JAR and the needed modules for your project, you can now do so.</p>
<p>The following modules, also available as Maven artifacts, have been created and extracted: <code>ant</code>, <code>bsf</code>, <code>console</code>, <code>docgenerator</code>, <code>groovydoc</code>, <code>groovysh</code>, <code>jms</code>, <code>json</code>, <code>jsr-223</code>, <code>servlet</code>, <code>sql</code>, <code>swing</code>, <code>templates</code>, <code>test</code>, <code>testng</code>, <code>xml</code>.</p>
<p>Also, it&rsquo;s possible for you to create your own extension modules, which will provide additional methods to JDK or third-party classes, just like Groovy does with the Groovy Development Kit.</p>
<p>For more information on this extension module system, please have a look at this <a href="http://docs.codehaus.org/display/GROOVY/Creating+an+extension+module">page</a> explaining the details.</p>
<p>We&rsquo;re looking forward to your feedback on this release! This is very important that you test this release within your projects to report anything, any bug or issue that you may encounter, so that we can make a great 2.0 release.</p>
<p>We&rsquo;re particularly interested in feedback on the invoke dynamic support, the static type checking, the static compilation, as well as the new modularity of Groovy.</p>
<p>Thanks a lot for your attention and precious time helping us releasing a great new milestone of the project!</p>
<p>And big thanks to all those who contributed to this release!</p>
<p>On behalf of the Groovy development team, we hope you&rsquo;ll all keep on groovy&rsquo;ing :-)</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Gaelyk 1.2 is out!</title><link>https://glaforge.dev/posts/2012/05/30/gaelyk-1-2-is-out/</link><pubDate>Wed, 30 May 2012 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/05/30/gaelyk-1-2-is-out/</guid><description>&lt;p>&lt;figure>
&lt;a href="#img-a8a115f16c1c59c9563131aaeb134fb7">
&lt;img src="https://glaforge.dev/img/misc/gaelyk.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-a8a115f16c1c59c9563131aaeb134fb7">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/gaelyk.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>I&amp;rsquo;m very pleased to announce the release of &lt;a href="http://gaelyk.appspot.com">Gaelyk 1.2&lt;/a>!&lt;/p>
&lt;p>For the impatient, you&amp;rsquo;ll be able to find the new JAR and Template Project here, as well as the changelog: &lt;a href="http://gaelyk.appspot.com/download">http://gaelyk.appspot.com/download&lt;/a>&lt;/p>
&lt;p>You&amp;rsquo;ll notice that the new template project uses Twitter Bootstrap now, to make it a bit fancier and nicer on the eye!&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-8ee262ba654747ddf881cb42409a9023">
&lt;img src="https://glaforge.dev/img/misc/gaelyk&amp;#43;template&amp;#43;bootstrap.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-8ee262ba654747ddf881cb42409a9023">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/gaelyk&amp;#43;template&amp;#43;bootstrap.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>We also upgraded to the latest and greatest of Groovy and App Engine&amp;rsquo;s SDK.&lt;/p></description><content:encoded>
<![CDATA[<p><figure>
  <a href="#img-a8a115f16c1c59c9563131aaeb134fb7">
    <img src="/img/misc/gaelyk.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-a8a115f16c1c59c9563131aaeb134fb7">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/gaelyk.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I&rsquo;m very pleased to announce the release of <a href="http://gaelyk.appspot.com">Gaelyk 1.2</a>!</p>
<p>For the impatient, you&rsquo;ll be able to find the new JAR and Template Project here, as well as the changelog: <a href="http://gaelyk.appspot.com/download">http://gaelyk.appspot.com/download</a></p>
<p>You&rsquo;ll notice that the new template project uses Twitter Bootstrap now, to make it a bit fancier and nicer on the eye!</p>
<p><figure>
  <a href="#img-8ee262ba654747ddf881cb42409a9023">
    <img src="/img/misc/gaelyk&#43;template&#43;bootstrap.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-8ee262ba654747ddf881cb42409a9023">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/gaelyk&#43;template&#43;bootstrap.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>We also upgraded to the latest and greatest of Groovy and App Engine&rsquo;s SDK.</p>
<p>In terms of new features, I&rsquo;d like to particularly highlight:</p>
<ul>
<li>the gradle gaelyk plugin offers <a href="https://github.com/bmuschko/gradle-gaelyk-plugin#tasks">pre-compilation of groovlets and templates</a> for faster startup times</li>
<li>the <a href="http://gaelyk.appspot.com/tutorial/app-engine-shortcuts#search">integration of the search service</a> with some nice syntax sugar for simplifying its usage</li>
<li>the <a href="http://gaelyk.appspot.com/tutorial/plugins#binaryplugins">binary plugins</a> feature</li>
<li>the <a href="http://gaelyk.appspot.com/tutorial/app-engine-shortcuts#pogo-entity-coercion-annotations"><code>@Entity</code> transform</a> to add datastore CRUD operations to your Groovy classes</li>
<li>the new <a href="http://gaelyk.appspot.com/tutorial/views-and-controllers#lazy"><code>geo</code> variables</a> in the binding to know where your users are coming from</li>
<li>and a few other improvements and fixes</li>
</ul>
<p>Thanks a lot to the Gaelyk committers, and in particular to Vladimir who helped me a lot on the finish line of this release!</p>
<p>Have fun with Gaelyk!</p>
<p>And we&rsquo;re looking forward to hearing about your feedback and experience with this new release.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Why coming to #GR8Conf?</title><link>https://glaforge.dev/posts/2012/05/10/why-coming-to-gr8conf/</link><pubDate>Thu, 10 May 2012 02:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/05/10/why-coming-to-gr8conf/</guid><description>&lt;p>I was reading &lt;a href="http://gr8conf.eu/blog/Interview-with-MrHaki">MrHaki&amp;rsquo;s interview&lt;/a> on the &lt;a href="http://gr8conf.eu">GR8Conf website&lt;/a> this morning, and he had some nice words and good points about why it&amp;rsquo;s important to attend the conference. You get to meet the creators and developers of the projects you&amp;rsquo;re interested in and have great content (emphasis mine):&lt;/p>
&lt;blockquote>
&lt;p>I am looking forward to meeting all the great people that make up the Groovy ecosystem. Everybody is always nice, friendly and ready to help. The atmosphere is always nice and laid back.&lt;/p></description><content:encoded>
<![CDATA[<p>I was reading <a href="http://gr8conf.eu/blog/Interview-with-MrHaki">MrHaki&rsquo;s interview</a> on the <a href="http://gr8conf.eu">GR8Conf website</a> this morning, and he had some nice words and good points about why it&rsquo;s important to attend the conference. You get to meet the creators and developers of the projects you&rsquo;re interested in and have great content (emphasis mine):</p>
<blockquote>
<p>I am looking forward to meeting all the great people that make up the Groovy ecosystem. Everybody is always nice, friendly and ready to help. The atmosphere is always nice and laid back.</p>
<p>The presentations always are high level and mostly done by the creators of the projects and frameworks. So the information is really from the source and that means you always get the latest information first at the conference.</p></blockquote>
<p>And on what attendees should expect (again emphasis mine):</p>
<blockquote>
<p>If you are using Groovy, Grails, Gradle or any of the other Groovy-related technologies, or you plan to do so, you should really come to the conference. This is your chance to meet the people that create the great products and use it in their daily development. You will get inside information and experience real-life usage of these technologies.</p>
<p>After the conference you will be inspired and ready to apply your newly learned knowledge in your daytime job or projects.</p></blockquote>
<p>I&rsquo;m looking forward to meeting you there!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Vert.x 1.0 released with its Groovy support</title><link>https://glaforge.dev/posts/2012/05/10/vert-x-1-0-released-with-its-groovy-support/</link><pubDate>Thu, 10 May 2012 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/05/10/vert-x-1-0-released-with-its-groovy-support/</guid><description>&lt;p>&lt;a href="https://twitter.com/#!/timfox">Tim Fox&lt;/a> yesterday announced &lt;a href="http://vertxproject.wordpress.com/2012/05/09/vert-x-1-0-final-is-released/">the release of Vert.x 1.0&lt;/a>.&lt;/p>
&lt;p>&lt;a href="http://vertx.io/">Vert.x&lt;/a> is a kind ofasynchronous application development environment and server. It works on the JVM, with JDK 7, and supports several languages like Java, &lt;a href="http://vertx.io/groovy_web_tutorial.html">Groovy&lt;/a>, Ruby or JavaScript.&lt;/p>
&lt;p>As the website puts it, vert.x can be defined along the following axis:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>polyglot&lt;/strong>: supporting Java, Groovy, Ruby and JavaScript or a mix and match of any of these even in a single application&lt;/li>
&lt;li>&lt;strong>simplicity&lt;/strong>: just a few lines of code to create your servers and components, without any XML configuration or anything like that, without being too simplistic&lt;/li>
&lt;li>&lt;strong>scalability&lt;/strong>: with Netty under the hood, with a message passing approach, it&amp;rsquo;s taking full advantage of the cores of your CPU(s)&lt;/li>
&lt;li>&lt;strong>concurrency&lt;/strong>: vert.x provides a simple concurrency-model so you don&amp;rsquo;t mess with traditional multithreaded programming&lt;/li>
&lt;/ul>
&lt;p>Vert.x competes directly with Node.js here, and Tim also positions it against the Play! 2 / Akka combo.&lt;/p></description><content:encoded>
<![CDATA[<p><a href="https://twitter.com/#!/timfox">Tim Fox</a> yesterday announced <a href="http://vertxproject.wordpress.com/2012/05/09/vert-x-1-0-final-is-released/">the release of Vert.x 1.0</a>.</p>
<p><a href="http://vertx.io/">Vert.x</a> is a kind ofasynchronous application development environment and server. It works on the JVM, with JDK 7, and supports several languages like Java, <a href="http://vertx.io/groovy_web_tutorial.html">Groovy</a>, Ruby or JavaScript.</p>
<p>As the website puts it, vert.x can be defined along the following axis:</p>
<ul>
<li><strong>polyglot</strong>: supporting Java, Groovy, Ruby and JavaScript or a mix and match of any of these even in a single application</li>
<li><strong>simplicity</strong>: just a few lines of code to create your servers and components, without any XML configuration or anything like that, without being too simplistic</li>
<li><strong>scalability</strong>: with Netty under the hood, with a message passing approach, it&rsquo;s taking full advantage of the cores of your CPU(s)</li>
<li><strong>concurrency</strong>: vert.x provides a simple concurrency-model so you don&rsquo;t mess with traditional multithreaded programming</li>
</ul>
<p>Vert.x competes directly with Node.js here, and Tim also positions it against the Play! 2 / Akka combo.</p>
<p>What&rsquo;s also worth looking at is the <a href="http://vertxproject.wordpress.com/2012/05/09/vert-x-vs-node-js-simple-http-benchmarks/">benchmark</a> that he ran against Node.js, showing that vert.x clearly outperforms Node.js. Also interesting is the fact that when using Groovy, there&rsquo;s just a very small overhead over using Java, although the simple benchmark didn&rsquo;t even use the upcoming static compilation feature of Groovy 2.0.</p>
<p>Congratulations to the vert.x team for the release!</p>
<p>I&rsquo;m impatient to play with it on <a href="http://cloudfoundry.com/">Cloud Foundry</a>, once it supports JDK 7.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Slides for Domain-Specific Languages in Groovy</title><link>https://glaforge.dev/talks/2012/05/03/slides-for-domain-specific-languages-in-groovy/</link><pubDate>Thu, 03 May 2012 00:00:00 +0200</pubDate><guid>https://glaforge.dev/talks/2012/05/03/slides-for-domain-specific-languages-in-groovy/</guid><description>&lt;p>During the Cloud Foundry Open Tour conference in Moscow, I presented a new presentation of &lt;a href="http://www.slideshare.net/glaforge/going-to-mars-with-groovy-domainspecific-languages">Domain-Specific Languages in Groovy&lt;/a>. I&amp;rsquo;ve already shared the link here and there (twitter, Google+, mailing-list), but not yet on my blog. So consder this fixed now. This new presentation is inspired from the beta DSL chapter from the second edition of Groovy in Action, where you&amp;rsquo;ll learn how to create a DSL for a little Mars robot. This is a pretty practical approach, bottom-up, as you progressively build the DSL, rather than listing all the existing techniques and illustrate them. I feel the flow is a bit better that way. Anyway&amp;hellip; enjoy:&lt;/p></description><content:encoded>
<![CDATA[<p>During the Cloud Foundry Open Tour conference in Moscow, I presented a new presentation of <a href="http://www.slideshare.net/glaforge/going-to-mars-with-groovy-domainspecific-languages">Domain-Specific Languages in Groovy</a>. I&rsquo;ve already shared the link here and there (twitter, Google+, mailing-list), but not yet on my blog. So consder this fixed now. This new presentation is inspired from the beta DSL chapter from the second edition of Groovy in Action, where you&rsquo;ll learn how to create a DSL for a little Mars robot. This is a pretty practical approach, bottom-up, as you progressively build the DSL, rather than listing all the existing techniques and illustrate them. I feel the flow is a bit better that way. Anyway&hellip; enjoy:</p>
<iframe
  style="width: 100%; aspect-ratio: 1.62" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"
  src="http://www.slideshare.net/slideshow/embed_code/12700368?rel=0" allowfullscreen webkitallowfullscreen mozallowfullscreen> </iframe>
<br><br>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy at Devoxx France</title><link>https://glaforge.dev/talks/2012/04/23/groovy-at-devoxx-france/</link><pubDate>Mon, 23 Apr 2012 02:00:00 +0200</pubDate><guid>https://glaforge.dev/talks/2012/04/23/groovy-at-devoxx-france/</guid><description>&lt;p>Last week saw the first edition of &lt;a href="http://www.devoxx.fr/display/FR12/Accueil">Devoxx France&lt;/a>. And what a success it was! The conference was sold out at 1200 persons. There was a great wealth of interesting topics and many passionate attendees to talk to. Overall, I come back home impressed that the first edition worked out so well, flawlessly, and was such a joy to attend. So a big thank you and congrats to all those involved in making Devoxx France such a great event!&lt;/p></description><content:encoded>
<![CDATA[<p>Last week saw the first edition of <a href="http://www.devoxx.fr/display/FR12/Accueil">Devoxx France</a>. And what a success it was! The conference was sold out at 1200 persons. There was a great wealth of interesting topics and many passionate attendees to talk to. Overall, I come back home impressed that the first edition worked out so well, flawlessly, and was such a joy to attend. So a big thank you and congrats to all those involved in making Devoxx France such a great event!</p>
<p>Along with <a href="http://www.jroller.com/melix/">Cédric Champeau</a>, I had the pleasure to present the novelties of Groovy 2.0 (and a bit of retrospective on some of the key features of Groovy 1.8). We covered quickly the invoke dynamic support and modularization, and went into more depth into the static type checking and static compilation aspects.</p>
<p>I&rsquo;ve pushed the <a href="http://www.slideshare.net/glaforge/groovy-20-devoxx-france-2012">slides of that presentation</a> on Slideshare, please see below for the embedded presentation.</p>
<p>What was also interesting was to update my informal benchmarks which looked at the performance of Groovy in a certain number of situations:</p>
<ul>
<li>without particular improvements (ie Groovy 1.7 and before),</li>
<li>with the primitive optimizations (Groovy 1.8),</li>
<li>and with static compilation.</li>
</ul>
<p>The results are pretty good and show that in static compilation mode, Groovy&rsquo;s performance is as good as Java in most cases. There are still some improvements we can make, some bytecode to clean up, some bugs to iron out, etc, but overall, the goal of attaining Java&rsquo;s performance is reached.</p>
<p>For my benchmark, I took some Java implementations of the Fibonaci suite, the calculation of Pi (quadrature style), and a binary tree implementation. I converted them to Groovy (often, it&rsquo;s just a file extension rename, plus removing some semicolons for good measure), without really trying to optimize them in a Groovy manner. And the results I got looked as follows:</p>
<p>Fibonaci Pi (quadrature) Binary trees Java 143 ms 93 ms 4.5 s Groovy with static compilation 145 ms 96 ms 6.9 s Groovy with primitive optimizations 271 ms 103 ms 29.6 s Groovy without optimizations 3200 ms 2590 ms 50.0 s So that&rsquo;s pretty good news for those who are looking for performance, and more generally it&rsquo;ll benefit anyone as well in any case.</p>
<p>And now&hellip; the slides!</p>
<iframe
  style="width: 100%; aspect-ratio: 1.62" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"
  src="http://www.slideshare.net/slideshow/embed_code/12628457?rel=0" allowfullscreen webkitallowfullscreen mozallowfullscreen> </iframe>
<br><br>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Run a Groovy script in Vi</title><link>https://glaforge.dev/posts/2012/04/23/run-a-groovy-script-in-vi/</link><pubDate>Mon, 23 Apr 2012 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/04/23/run-a-groovy-script-in-vi/</guid><description>&lt;p>In the Vi vs Emacs war, I&amp;rsquo;m in the Vi camp. I&amp;rsquo;m using Vim on the command-line to quickly edit files, and I&amp;rsquo;m also using MacVim on my Mac as my text editor. When I&amp;rsquo;m not using an IDE or the Groovy Console, I also want to be able to edit and run my Groovy scripts in my text editor. It&amp;rsquo;s not too difficult to run a Groovy script from Vi, you can simply do:&lt;/p></description><content:encoded>
<![CDATA[<p>In the Vi vs Emacs war, I&rsquo;m in the Vi camp. I&rsquo;m using Vim on the command-line to quickly edit files, and I&rsquo;m also using MacVim on my Mac as my text editor. When I&rsquo;m not using an IDE or the Groovy Console, I also want to be able to edit and run my Groovy scripts in my text editor. It&rsquo;s not too difficult to run a Groovy script from Vi, you can simply do:</p>
<pre tabindex="0"><code class="language-vi" data-lang="vi">:!groovy %
</code></pre><p>You use the colon to enter the command mode, then use the bang to issue a shell command. Here, obviously, I&rsquo;m going to run the groovy command. And I can then pass the script I&rsquo;m editing as parameter thanks to the percent sign, which points at the current file in my buffer.</p>
<p>The drawback is that the output is the command area, but I wanted to get the output in its own buffer. So I spent a bit of time to flesh out my Vi skills, and came up with a function to open a new scratch buffer and output the result of the script execution in that new buffer, with the following script that I added in my <code>.vimrc</code> configuration:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// the function to run my groovy scripts 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">function</span><span style="color:#666">!</span> RunGroovy() 
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// copy the current buffer file name in a variable 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#007020;font-weight:bold">let</span> gfname<span style="color:#666">=</span><span style="">@</span><span style="color:#666">%</span> 
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// open a new buffer in my window below 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    botright <span style="color:#007020;font-weight:bold">new</span> 
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// define the buffer to be a mere scratch buffer not intended to be edited or saved 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    setlocal buftype<span style="color:#666">=</span>nofile bufhidden<span style="color:#666">=</span>wipe nobuflisted noswapfile nowrap 
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// create a temporaty file name to hold the output of the execution of my Groovy script 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#007020;font-weight:bold">let</span> gtmpf <span style="color:#666">=</span> tempname() 
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// define the command line to launch my Groovy script and retrieve its output in the temporary file 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#007020;font-weight:bold">let</span> gcmd <span style="color:#666">=</span> <span style="color:#4070a0">&#39;:!groovy &#39;</span> . gfname . <span style="color:#4070a0">&#39; &gt; &#39;</span> . gtmpf 
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// execute the groovy command 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    silent execute gcmd 
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// insert the content of the output from the temporary file in my buffer 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    silent execute <span style="color:#4070a0">&#39;0r &#39;</span> . gtmpf
</span></span><span style="display:flex;"><span>endfunction   
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// add F5 as the shortcut for executing my Groovy scripts 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>map <span style="color:#666">:</span>call RunGroovy()
</span></span></code></pre></div><p>I&rsquo;m definitely not a Vi expert, and this script can be improved greatly probably. But it&rsquo;s good enough for now. But I&rsquo;m open to improvements if the Vi experts reading this blog want to join in and give me advice!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Come to GR8Conf for half the price!</title><link>https://glaforge.dev/posts/2012/04/13/come-to-gr8conf-for-half-the-price/</link><pubDate>Fri, 13 Apr 2012 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/04/13/come-to-gr8conf-for-half-the-price/</guid><description>&lt;p>This is this time of the year where all the Groovy hackers and fans are meeting together in Denmark, Copenhagen, for the &lt;a href="http://gr8conf.eu/">GR8Conf conference&lt;/a>. A conference dedicated to &lt;a href="http://groovy.codehaus.org">Groovy&lt;/a>, Grails, Gradle, Griffon, Spock, GPars and all the Groovy ecosystem. If you&amp;rsquo;re interested in these technologies or are already using them, this is the opportunity you shouldn&amp;rsquo;t miss.&lt;/p>
&lt;p>Also, you&amp;rsquo;re pretty lucky, because I have a ticket for half the price to offer you! But there&amp;rsquo;s just one seat I can offer at that price. So if you&amp;rsquo;re interested, please add a nice comment to this post, that may be fun, or explaining why you&amp;rsquo;d like to come, and in a few weeks, I&amp;rsquo;ll tell you who deserves to win this prize!&lt;/p></description><content:encoded>
<![CDATA[<p>This is this time of the year where all the Groovy hackers and fans are meeting together in Denmark, Copenhagen, for the <a href="http://gr8conf.eu/">GR8Conf conference</a>. A conference dedicated to <a href="http://groovy.codehaus.org">Groovy</a>, Grails, Gradle, Griffon, Spock, GPars and all the Groovy ecosystem. If you&rsquo;re interested in these technologies or are already using them, this is the opportunity you shouldn&rsquo;t miss.</p>
<p>Also, you&rsquo;re pretty lucky, because I have a ticket for half the price to offer you! But there&rsquo;s just one seat I can offer at that price. So if you&rsquo;re interested, please add a nice comment to this post, that may be fun, or explaining why you&rsquo;d like to come, and in a few weeks, I&rsquo;ll tell you who deserves to win this prize!</p>
<p>Ready, Steady, Groovy!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>bash tip: find and grep through files</title><link>https://glaforge.dev/posts/2012/03/29/bash-tip-find-and-grep-through-files/</link><pubDate>Thu, 29 Mar 2012 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/03/29/bash-tip-find-and-grep-through-files/</guid><description>&lt;p>It happens once in a while that I want to find all files containing a certain string. I know command-line tools such as grep, cat, and find, but I never remember the right combination to achieve that task. So as to remember and reference it later, I write this small blog post to remind me how to do it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>find . -type f -exec grep YOURSTRING /dev/null &lt;span style="color:#666">{}&lt;/span> &lt;span style="color:#4070a0;font-weight:bold">\;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>find .&lt;/code> part will search from the current directory (and all its subdirectories), &lt;code>- type f&lt;/code> to search for files (not directories or links, etc), &lt;code>-exec&lt;/code> to use the grep command to find through the files, with YOURSTRING as query string, /dev/null to throw away the errors you don&amp;rsquo;t care about, and &lt;code>{}&lt;/code> is the current file to search into with grep.&lt;/p></description><content:encoded>
<![CDATA[<p>It happens once in a while that I want to find all files containing a certain string. I know command-line tools such as grep, cat, and find, but I never remember the right combination to achieve that task. So as to remember and reference it later, I write this small blog post to remind me how to do it:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>find . -type f -exec grep YOURSTRING /dev/null <span style="color:#666">{}</span> <span style="color:#4070a0;font-weight:bold">\;</span>
</span></span></code></pre></div><p>The <code>find .</code> part will search from the current directory (and all its subdirectories), <code>- type f</code> to search for files (not directories or links, etc), <code>-exec</code> to use the grep command to find through the files, with YOURSTRING as query string, /dev/null to throw away the errors you don&rsquo;t care about, and <code>{}</code> is the current file to search into with grep.</p>
<p><strong>Update:</strong> a simple approach proposed in the comments is:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>grep -r YOURSTRING <span style="color:#4070a0;font-weight:bold">\*</span>
</span></span></code></pre></div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Incomplete string regex matching</title><link>https://glaforge.dev/posts/2012/03/27/incomplete-string-regex-matching/</link><pubDate>Tue, 27 Mar 2012 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2012/03/27/incomplete-string-regex-matching/</guid><description>&lt;p>Once in a while, I stumble upon the need of being able to tell whether a string could match a certain regular expression pattern, if it were given some more characters.&lt;/p>
&lt;p>For example, a user is typing a time in an input field, following the pattern of 2 digits, a colon, and 2 other digits. He enters the first two digits and the colon, and we&amp;rsquo;re wondering if the time could be valid if he entered some more keystrokes.&lt;/p></description><content:encoded>
<![CDATA[<p>Once in a while, I stumble upon the need of being able to tell whether a string could match a certain regular expression pattern, if it were given some more characters.</p>
<p>For example, a user is typing a time in an input field, following the pattern of 2 digits, a colon, and 2 other digits. He enters the first two digits and the colon, and we&rsquo;re wondering if the time could be valid if he entered some more keystrokes.</p>
<p>I found the <a href="http://jregex.sourceforge.net/">JRegex</a> project and its documentation about <a href="http://jregex.sourceforge.net/gstarted-advanced.html#imatching">incomplete matching</a> which showed me that it was possible to achieve such a goal. But using JRegex is adding another library on the classpath for such a small requirement, that I started wondering if there was a way to emulate that with Java&rsquo;s own regex support.</p>
<p>We&rsquo;re used to methods like <a href="http://docs.oracle.com/javase/6/docs/api/java/util/regex/Matcher.html#matches()"><code>matches()</code></a> / <a href="http://docs.oracle.com/javase/6/docs/api/java/util/regex/Matcher.html#find()"><code>find()</code></a> on <a href="http://docs.oracle.com/javase/6/docs/api/java/util/regex/Matcher.html"><code>Matcher</code></a>, but not so much with some of the other methods. I thought <a href="http://docs.oracle.com/javase/6/docs/api/java/util/regex/Matcher.html#lookingAt()"><code>lookingAt()</code></a> could be of help, but it&rsquo;s only interesting if you want to check that the beginning of an input string matches a pattern&hellip; whereas we want a full string to match the beginning of a pattern instead.</p>
<p>After twitting about my quest, Fred Martini came up with the <a href="https://twitter.com/#!/adiguba/status/184649138464493569">solution</a> to my problem. Thanks Fred! And the winner is&hellip; <a href="http://docs.oracle.com/javase/6/docs/api/java/util/regex/Matcher.html#hitEnd()"><code>hitEnd()</code></a>. Looking at the JavaDoc, it&rsquo;s not that explicit how it could be used&hellip;</p>
<blockquote>
<p><code>public boolean hitEnd()</code></p>
<p>Returns true if the end of input was hit by the search engine in the last match operation performed by this matcher.</p>
<p>When this method returns true, then it is possible that more input would have changed the result of the last search.</p>
<p><strong>Returns:</strong> true iff the end of input was hit in the last match; false otherwise</p></blockquote>
<p>When the matcher is trying to match the pattern against the input stream, it arrives at the end of the input stream before it could really say if the string matched that pattern or not. And as the JavaDoc says, perhaps that with some more input, the search could have succeeded.</p>
<p>More concretely, let&rsquo;s have a look at a quick example, coded in Groovy for simplicity sake:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> input <span style="color:#666">=</span> <span style="color:#4070a0">/12:/</span> 
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> pattern <span style="color:#666">=</span> Pattern<span style="color:#666">.</span><span style="color:#4070a0">compile</span><span style="color:#666">(</span><span style="color:#4070a0">/\\d{2}:\\d{2}/</span><span style="color:#666">)</span> 
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> matcher <span style="color:#666">=</span> pattern<span style="color:#666">.</span><span style="color:#4070a0">matcher</span><span style="color:#666">(</span>input<span style="color:#666">)</span> 
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> matcher<span style="color:#666">.</span><span style="color:#4070a0">matches</span><span style="color:#666">()</span> <span style="color:#666">||</span> matcher<span style="color:#666">.</span><span style="color:#4070a0">hitEnd</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>Or for a more idiomatic Groovy version:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> matcher <span style="color:#666">=</span> <span style="color:#4070a0">/12:/</span> <span style="color:#666">=~</span> <span style="color:#4070a0">/\\d{2}:\\d{2}/</span>  
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> matcher<span style="color:#666">.</span><span style="color:#4070a0">matches</span><span style="color:#666">()</span> <span style="color:#666">||</span> matcher<span style="color:#666">.</span><span style="color:#4070a0">hitEnd</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>Thinking about it&hellip; we could even add a <code>matchesPartially()</code> to the GDK!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 2.0 presentation at 33rd Degree</title><link>https://glaforge.dev/talks/2012/03/21/groovy-2-0-presentation-at-33rd-degree/</link><pubDate>Wed, 21 Mar 2012 00:00:00 +0100</pubDate><guid>https://glaforge.dev/talks/2012/03/21/groovy-2-0-presentation-at-33rd-degree/</guid><description>&lt;p>Here are the slides of the Groovy 2.0 presentation I&amp;rsquo;ve given at the 33rd Degree conference in Krakow.&lt;/p>
&lt;iframe
style="width: 100%; aspect-ratio: 1.62" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"
src="http://www.slideshare.net/slideshow/embed_code/12092341?rel=0" allowfullscreen webkitallowfullscreen mozallowfullscreen> &lt;/iframe>
&lt;br>&lt;br></description><content:encoded>
<![CDATA[<p>Here are the slides of the Groovy 2.0 presentation I&rsquo;ve given at the 33rd Degree conference in Krakow.</p>
<iframe
  style="width: 100%; aspect-ratio: 1.62" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"
  src="http://www.slideshare.net/slideshow/embed_code/12092341?rel=0" allowfullscreen webkitallowfullscreen mozallowfullscreen> </iframe>
<br><br>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Speaking of Groovy 2.0 at 33rd Degree, in Poland</title><link>https://glaforge.dev/posts/2012/03/18/speaking-of-groovy-2-0-at-33rd-degree-in-poland/</link><pubDate>Sun, 18 Mar 2012 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2012/03/18/speaking-of-groovy-2-0-at-33rd-degree-in-poland/</guid><description>&lt;p>Tomorrow I&amp;rsquo;m flying to Krakow, in Poland, to &lt;a href="http://2012.33degree.org/talk/show/17">speak about the upcoming Groovy 2.0&lt;/a> release, at the &lt;a href="http://2012.33degree.org/">33rd Degree conference&lt;/a>. I&amp;rsquo;m looking forward to meeting some of you there!&lt;br />
Here&amp;rsquo;s my abstract for What&amp;rsquo;s new in Groovy 2.0?&lt;/p>
&lt;blockquote>
&lt;p>After a quick look at the key features of Groovy 1.8, including more readable Domain-Specific Languages, concurrency capabilities with the GPars library, and built-in JSON support, we&amp;rsquo;ll dive right into the most important features of Groovy 2.0!&lt;br />
A lot of the Groovy users are using the language as a Java scripting language, or sometimes as a better Java. First of all, not all of them need the dynamic capabilities, nor do they need all the library improvements provided by Groovy. For the latter, Groovy becomes more modular with smaller core modules that you can compose. For the former, in addition to its usual dynamic features, Groovy 2.0 adds static type checking, allowing you to ensure the correctness of your code before it ships and quickly spot early errors at compile time.&lt;br />
Also, not everybody needs dynamic features at all times, and the natural extension to static type checking is static compilation. With static compilation, Groovy 2.0 generates fast and performant bytecode like Java, and shields key parts of your code base from possible interferences of dynamic features, making critical paths both faster and immune to monkey patching. But when you need fast and performant dynamic logic, Groovy 2.0 also features the integration of the &amp;ldquo;invoke dynamic&amp;rdquo; support coming up with JDK 7 and beyond.&lt;/p></description><content:encoded>
<![CDATA[<p>Tomorrow I&rsquo;m flying to Krakow, in Poland, to <a href="http://2012.33degree.org/talk/show/17">speak about the upcoming Groovy 2.0</a> release, at the <a href="http://2012.33degree.org/">33rd Degree conference</a>. I&rsquo;m looking forward to meeting some of you there!<br />
Here&rsquo;s my abstract for What&rsquo;s new in Groovy 2.0?</p>
<blockquote>
<p>After a quick look at the key features of Groovy 1.8, including more readable Domain-Specific Languages, concurrency capabilities with the GPars library, and built-in JSON support, we&rsquo;ll dive right into the most important features of Groovy 2.0!<br />
A lot of the Groovy users are using the language as a Java scripting language, or sometimes as a better Java. First of all, not all of them need the dynamic capabilities, nor do they need all the library improvements provided by Groovy. For the latter, Groovy becomes more modular with smaller core modules that you can compose. For the former, in addition to its usual dynamic features, Groovy 2.0 adds static type checking, allowing you to ensure the correctness of your code before it ships and quickly spot early errors at compile time.<br />
Also, not everybody needs dynamic features at all times, and the natural extension to static type checking is static compilation. With static compilation, Groovy 2.0 generates fast and performant bytecode like Java, and shields key parts of your code base from possible interferences of dynamic features, making critical paths both faster and immune to monkey patching. But when you need fast and performant dynamic logic, Groovy 2.0 also features the integration of the &ldquo;invoke dynamic&rdquo; support coming up with JDK 7 and beyond.</p></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Mac trick: change Time Machine backup interval</title><link>https://glaforge.dev/posts/2012/03/14/mac-trick-change-time-machine-backup-interval/</link><pubDate>Wed, 14 Mar 2012 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2012/03/14/mac-trick-change-time-machine-backup-interval/</guid><description>&lt;p>If you think Time Machine is backing up your Mac too often, you can customize the interval between two automatic scheduled backups. I found that trick on &lt;a href="http://www.macyourself.com/2010/02/21/how-to-change-time-machine-backup-interval-backup-manually/">MacYourself&lt;/a>.&lt;/p>
&lt;p>Open up a Terminal, and change the value, in seconds, of the interval (here 7200 seconds == 2 hours instead of the standard one hour):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>sudo defaults write /System/Library/LaunchDaemons/com.apple.backupd-auto StartInterval -int &lt;span style="color:#40a070">7200&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Update:&lt;/strong> This doesn&amp;rsquo;t seem to work on Mac OS X Lion, although it worked in previous versions (like Leopard). Till we find a good workaround, Thomas, in the comments, suggested looking at this nice little utility: &lt;a href="http://www.klieme.com/TimeMachineScheduler.html">Time Machine Scheduler&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>If you think Time Machine is backing up your Mac too often, you can customize the interval between two automatic scheduled backups. I found that trick on <a href="http://www.macyourself.com/2010/02/21/how-to-change-time-machine-backup-interval-backup-manually/">MacYourself</a>.</p>
<p>Open up a Terminal, and change the value, in seconds, of the interval (here 7200 seconds == 2 hours instead of the standard one hour):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo defaults write /System/Library/LaunchDaemons/com.apple.backupd-auto StartInterval -int <span style="color:#40a070">7200</span>
</span></span></code></pre></div><p><strong>Update:</strong> This doesn&rsquo;t seem to work on Mac OS X Lion, although it worked in previous versions (like Leopard). Till we find a good workaround, Thomas, in the comments, suggested looking at this nice little utility: <a href="http://www.klieme.com/TimeMachineScheduler.html">Time Machine Scheduler</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy news bits: API additions, contributing to the project, Eclipse plugin</title><link>https://glaforge.dev/posts/2012/03/05/groovy-news-bits-api-additions-contributing-to-the-project-eclipse-plugin/</link><pubDate>Mon, 05 Mar 2012 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2012/03/05/groovy-news-bits-api-additions-contributing-to-the-project-eclipse-plugin/</guid><description>&lt;p>With the recent &lt;a href="https://glaforge.dev/posts/2012/02/09/groovy-1-8-6-released/">release of Groovy 1.8.6&lt;/a>, Groovy contributor Tim Yates, posted a few blog posts about some of the little useful enhancements that found their way in the release:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://blog.bloidonia.com/post/18186368994/whats-new-in-groovy-1-8-6-array-contains">&lt;code>Array#contains()&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://blog.bloidonia.com/post/18125339463/whats-new-in-groovy-1-8-6-byte-encodehex">&lt;code>Byte[]#encodeHex()&lt;/code>&lt;/a>&lt;/li>
&lt;li>the very useful &lt;a href="http://blog.bloidonia.com/post/18073244930/whats-new-in-groovy-1-8-6-the-collate-method">&lt;code>collate()&lt;/code> method&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Tim also wrote a really great and detailed blog post explaining &lt;a href="http://blog.bloidonia.com/post/17711808015/submitting-code-to-the-groovy-language">how to contribute to the Groovy project&lt;/a>. Be sure to check it out if you&amp;rsquo;ve always wanted to contribute to the project. Tim shows how to get started, to propose a new feature or bug fix through JIRA, how to submit a pull request through GitHub, etc.&lt;/p></description><content:encoded>
<![CDATA[<p>With the recent <a href="https://glaforge.dev/posts/2012/02/09/groovy-1-8-6-released/">release of Groovy 1.8.6</a>, Groovy contributor Tim Yates, posted a few blog posts about some of the little useful enhancements that found their way in the release:</p>
<ul>
<li><a href="http://blog.bloidonia.com/post/18186368994/whats-new-in-groovy-1-8-6-array-contains"><code>Array#contains()</code></a></li>
<li><a href="http://blog.bloidonia.com/post/18125339463/whats-new-in-groovy-1-8-6-byte-encodehex"><code>Byte[]#encodeHex()</code></a></li>
<li>the very useful <a href="http://blog.bloidonia.com/post/18073244930/whats-new-in-groovy-1-8-6-the-collate-method"><code>collate()</code> method</a></li>
</ul>
<p>Tim also wrote a really great and detailed blog post explaining <a href="http://blog.bloidonia.com/post/17711808015/submitting-code-to-the-groovy-language">how to contribute to the Groovy project</a>. Be sure to check it out if you&rsquo;ve always wanted to contribute to the project. Tim shows how to get started, to propose a new feature or bug fix through JIRA, how to submit a pull request through GitHub, etc.</p>
<p><a href="http://blog.bloidonia.com/">Tim Yates&rsquo; blog</a> is worth subscribing to!</p>
<p>And last but no least, hot from the oven, Andrew Eisenberg from the Groovy Eclipse team covers the <a href="http://docs.codehaus.org/display/GROOVY/Groovy-Eclipse+2.6.1+New+and+Noteworthy">latest enhancements to the Groovy Eclipse plugin</a>: with various inferencing enhancements, new quick fixes and quick assists, and more.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Don't forget the GR8Conf Call for Papers</title><link>https://glaforge.dev/posts/2012/02/09/don-t-forget-the-gr8conf-call-for-papers/</link><pubDate>Thu, 09 Feb 2012 10:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2012/02/09/don-t-forget-the-gr8conf-call-for-papers/</guid><description>&lt;p>(&lt;a href="http://gr8conf.org/)">http://gr8conf.org/)&lt;/a>, the Groovy ecosystem conference series, are coming to Denmark and to the USA in a few months. You&amp;rsquo;ll learn about the latest novelties about tons of great Groovy-based technologies, like Groovy, Grails, Gaelyk, Gradle, GPars, Griffon, Spock, CodeNarc and more. It&amp;rsquo;s the best place to engage with the community, get to know the developers of those projects, and present your success stories, and more.&lt;/p>
&lt;p>If you&amp;rsquo;re willing to speak at the conference about all those topics, please head over to the GR8Conf Call for Papers. And hurry up, there&amp;rsquo;s less than one week left!&lt;/p></description><content:encoded>
<![CDATA[<p>(<a href="http://gr8conf.org/)">http://gr8conf.org/)</a>, the Groovy ecosystem conference series, are coming to Denmark and to the USA in a few months. You&rsquo;ll learn about the latest novelties about tons of great Groovy-based technologies, like Groovy, Grails, Gaelyk, Gradle, GPars, Griffon, Spock, CodeNarc and more. It&rsquo;s the best place to engage with the community, get to know the developers of those projects, and present your success stories, and more.</p>
<p>If you&rsquo;re willing to speak at the conference about all those topics, please head over to the GR8Conf Call for Papers. And hurry up, there&rsquo;s less than one week left!</p>
<ul>
<li><a href="http://gr8conf.eu/cfp">GR8Conf Europe Call for Paper</a></li>
<li><a href="http://gr8conf.us/Presentation-Proposal">GR8Conf US Call for Paper</a></li>
</ul>
<p>I&rsquo;m really looking forward to seeing you there!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 1.8.6 released</title><link>https://glaforge.dev/posts/2012/02/09/groovy-1-8-6-released/</link><pubDate>Thu, 09 Feb 2012 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2012/02/09/groovy-1-8-6-released/</guid><description>&lt;p>The Groovy development team is pleased to announce the release of Groovy 1.8.6.&lt;/p>
&lt;p>Groovy 1.8.6 is a maintenance release essentially, with a few minor improvements and new features.&lt;/p>
&lt;p>You can see the details in the JIRA release notes here:&lt;br />
&lt;a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;version=18245">http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;version=18245&lt;/a>&lt;/p>
&lt;p>You can download Groovy 1.8.6 in the Download section of the Groovy website:&lt;br />
&lt;a href="http://groovy.codehaus.org/Download">http://groovy.codehaus.org/Download&lt;/a>&lt;/p>
&lt;p>Thanks a lot to all those who contributed to this release!&lt;/p>
&lt;p>Next in line should be a new beta of Groovy 2.0 ;-)&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy development team is pleased to announce the release of Groovy 1.8.6.</p>
<p>Groovy 1.8.6 is a maintenance release essentially, with a few minor improvements and new features.</p>
<p>You can see the details in the JIRA release notes here:<br />
<a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=18245">http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=18245</a></p>
<p>You can download Groovy 1.8.6 in the Download section of the Groovy website:<br />
<a href="http://groovy.codehaus.org/Download">http://groovy.codehaus.org/Download</a></p>
<p>Thanks a lot to all those who contributed to this release!</p>
<p>Next in line should be a new beta of Groovy 2.0 ;-)</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Static type checking and compilation in Groovy 2</title><link>https://glaforge.dev/posts/2012/01/31/static-type-checking-and-compilation-in-groovy-2/</link><pubDate>Tue, 31 Jan 2012 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2012/01/31/static-type-checking-and-compilation-in-groovy-2/</guid><description>&lt;p>&lt;a href="https://twitter.com/cedricchampeau">Cédric Champeau&lt;/a>, the Groovy core developer working on static type checking and static compilation for Groovy 2, recently gave a very interesting and instructive presentation on this topic at the &lt;a href="http://www.meetup.com/Paris-Groovy-Grails/">Paris Groovy / Grails User Group&lt;/a>, at the VMware offices.&lt;/p>
&lt;p>You can have a look at his &lt;a href="http://www.jroller.com/melix/entry/static_type_checking_talk_from">blog post about the presentation&lt;/a>, including some additional comments regarding the method dispatch logic, some status update and directions.&lt;/p>
&lt;p>And you should also have a look at Cédric&amp;rsquo;s &lt;a href="http://www.jroller.com/melix/entry/static_compilation_for_groovy_poll">analysis of the polls&lt;/a> embedded in the presentation which asked a few questions about people&amp;rsquo;s preferences and expectations with regards to statuc type checking and compilation. Don&amp;rsquo;t hesitate to add your own input, as the poll is not closed and feedback is always welcome.&lt;/p></description><content:encoded>
<![CDATA[<p><a href="https://twitter.com/cedricchampeau">Cédric Champeau</a>, the Groovy core developer working on static type checking and static compilation for Groovy 2, recently gave a very interesting and instructive presentation on this topic at the <a href="http://www.meetup.com/Paris-Groovy-Grails/">Paris Groovy / Grails User Group</a>, at the VMware offices.</p>
<p>You can have a look at his <a href="http://www.jroller.com/melix/entry/static_type_checking_talk_from">blog post about the presentation</a>, including some additional comments regarding the method dispatch logic, some status update and directions.</p>
<p>And you should also have a look at Cédric&rsquo;s <a href="http://www.jroller.com/melix/entry/static_compilation_for_groovy_poll">analysis of the polls</a> embedded in the presentation which asked a few questions about people&rsquo;s preferences and expectations with regards to statuc type checking and compilation. Don&rsquo;t hesitate to add your own input, as the poll is not closed and feedback is always welcome.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>GR8Conf Europe Call for Paper open</title><link>https://glaforge.dev/posts/2012/01/12/gr8conf-europe-call-for-paper-open/</link><pubDate>Thu, 12 Jan 2012 02:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2012/01/12/gr8conf-europe-call-for-paper-open/</guid><description>&lt;p>&lt;a href="http://gr8conf.eu/content/eu2012/index">GR8Conf Europe 2012&lt;/a> will take place in Copenhagen, on June 6th-8th. It&amp;rsquo;s the best place to meet all the Groovy folks, from Groovy itself, and from the wealth of the Groovy Ecosystem (Grails, Gradle, GPars, Griffon, Spock, GContracts, CodeNarc, Gaelyk/Caelyf, etc). It&amp;rsquo;s also the best opportunity to stay up-to-date with the latest developments, and discover what&amp;rsquo;s cooking!&lt;/p>
&lt;p>The Call for Paper is open. So don&amp;rsquo;t hesitate to &lt;a href="http://gr8conf.eu/content/eu2012/cfp">submit your proposal&lt;/a>.&lt;/p>
&lt;p>Some useful links:&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://gr8conf.eu/content/eu2012/index">GR8Conf Europe 2012</a> will take place in Copenhagen, on June 6th-8th. It&rsquo;s the best place to meet all the Groovy folks, from Groovy itself, and from the wealth of the Groovy Ecosystem (Grails, Gradle, GPars, Griffon, Spock, GContracts, CodeNarc, Gaelyk/Caelyf, etc). It&rsquo;s also the best opportunity to stay up-to-date with the latest developments, and discover what&rsquo;s cooking!</p>
<p>The Call for Paper is open. So don&rsquo;t hesitate to <a href="http://gr8conf.eu/content/eu2012/cfp">submit your proposal</a>.</p>
<p>Some useful links:</p>
<ul>
<li><a href="http://gr8conf.eu/content/eu2012/index">GR8Conf Europe website</a></li>
<li>GR8Conf Europe <a href="http://gr8conf.eu/content/eu2012/cfp">Call for Paper</a></li>
<li><a href="https://twitter.com/#!/gr8conf">Follow @GR8Conf</a> on Twitter to be notified about the latest news</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Mon One Man Show au Paris JUG</title><link>https://glaforge.dev/posts/2012/01/12/mon-one-man-show-au-paris-jug/</link><pubDate>Thu, 12 Jan 2012 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2012/01/12/mon-one-man-show-au-paris-jug/</guid><description>&lt;p>Lors de l&amp;rsquo;&lt;a href="http://lescastcodeurs.com/2011/12/les-cast-codeurs-podcasts-episodes-50-lanniversaire/">enregistrement du 50ème&lt;/a> épisode des &lt;a href="http://lescastcodeurs.com/">Cast Codeurs&lt;/a> au &lt;a href="http://www.parisjug.org/xwiki/bin/view/Main/WebHome">Paris JUG&lt;/a>, j&amp;rsquo;ai eu le plaisir de faire un petit &amp;ldquo;one man show&amp;rdquo; comique (enfin j&amp;rsquo;ai essayé) sur notre métier d&amp;rsquo;informaticien, les tendances du moment, les news et autres scoops, etc.&lt;/p>
&lt;p>Cette idée est partie d&amp;rsquo;une discussion avec &lt;a href="http://www.touilleur-express.fr/">Nicolas Martignole&lt;/a> il y a 6 mois où l&amp;rsquo;on s&amp;rsquo;était dit que ce serait amusant de plaisanter un peu sur notre métier, les technos que l&amp;rsquo;on utilise, etc, et d&amp;rsquo;en faire une présentation humoristique. Et encouragé par les Cast Codeurs, je me suis lancé ce défi d&amp;rsquo;essayer de faire rire mon auditoire. A vous de juger si j&amp;rsquo;ai réussi ! Et je tiens à dire que les rires que vous entendrez sur l&amp;rsquo;enregistrement n&amp;rsquo;ont pas été rajoutés après coup ;-)&lt;/p></description><content:encoded>
<![CDATA[<p>Lors de l&rsquo;<a href="http://lescastcodeurs.com/2011/12/les-cast-codeurs-podcasts-episodes-50-lanniversaire/">enregistrement du 50ème</a> épisode des <a href="http://lescastcodeurs.com/">Cast Codeurs</a> au <a href="http://www.parisjug.org/xwiki/bin/view/Main/WebHome">Paris JUG</a>, j&rsquo;ai eu le plaisir de faire un petit &ldquo;one man show&rdquo; comique (enfin j&rsquo;ai essayé) sur notre métier d&rsquo;informaticien, les tendances du moment, les news et autres scoops, etc.</p>
<p>Cette idée est partie d&rsquo;une discussion avec <a href="http://www.touilleur-express.fr/">Nicolas Martignole</a> il y a 6 mois où l&rsquo;on s&rsquo;était dit que ce serait amusant de plaisanter un peu sur notre métier, les technos que l&rsquo;on utilise, etc, et d&rsquo;en faire une présentation humoristique. Et encouragé par les Cast Codeurs, je me suis lancé ce défi d&rsquo;essayer de faire rire mon auditoire. A vous de juger si j&rsquo;ai réussi ! Et je tiens à dire que les rires que vous entendrez sur l&rsquo;enregistrement n&rsquo;ont pas été rajoutés après coup ;-)</p>
<p>J&rsquo;en profite pour dire un grand merci à Charles et David pour l&rsquo;enregistrement et la post-production pour pouvoir offrir <a href="http://www.parleys.com/#st=5&amp;id=2957&amp;sl=0">cette présentation sur Parleys</a> !</p>
<p>La présentation commence vraiment après les 2 premières minutes, à cause de quelques petits problèmes techniques avec mon ordinateur.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Hans Dockter interview on Gradle</title><link>https://glaforge.dev/posts/2012/01/10/hans-dokter-interview-on-gradle/</link><pubDate>Tue, 10 Jan 2012 00:10:00 +0100</pubDate><guid>https://glaforge.dev/posts/2012/01/10/hans-dokter-interview-on-gradle/</guid><description>&lt;p>While at the &lt;a href="https://glaforge.dev/posts/2011/12/17/videos-and-slides-of-groovy-grails-exchange-2011/">Groovy/Grails eXchange conference&lt;/a> in London last month, I had the pleasure and honour to interview &lt;a href="http://gradleware.com/team">Hans Dockter&lt;/a>, creator of &lt;a href="http://gradle.org">Gradle&lt;/a> and CEO of &lt;a href="http://gradleware.com/">GradleWare&lt;/a>. This &lt;a href="http://skillsmatter.com/podcast/home/interview-on-gradle/js-3210">interview was recorded&lt;/a> and published by &lt;a href="http://skillsmatter.com/">SkillsMatter&lt;/a>.&lt;/p>
&lt;p>Here&amp;rsquo;s a list of questions I had the chance to ask Hans:&lt;/p>
&lt;ul>
&lt;li>What&amp;rsquo;s Gradle?&lt;/li>
&lt;li>Any new about Gradle 1.0 and what&amp;rsquo;s being worked on?&lt;/li>
&lt;li>Why is Gradle using Groovy for its DSL?&lt;/li>
&lt;li>Is it possible to enforce conventions and standards à la Maven?&lt;/li>
&lt;li>Some words on IDE integration, roadmap, etc?&lt;/li>
&lt;/ul>
&lt;p>Check the &lt;a href="http://skillsmatter.com/podcast/home/interview-on-gradle/js-3210">video&lt;/a>!&lt;/p></description><content:encoded>
<![CDATA[<p>While at the <a href="https://glaforge.dev/posts/2011/12/17/videos-and-slides-of-groovy-grails-exchange-2011/">Groovy/Grails eXchange conference</a> in London last month, I had the pleasure and honour to interview <a href="http://gradleware.com/team">Hans Dockter</a>, creator of <a href="http://gradle.org">Gradle</a> and CEO of <a href="http://gradleware.com/">GradleWare</a>. This <a href="http://skillsmatter.com/podcast/home/interview-on-gradle/js-3210">interview was recorded</a> and published by <a href="http://skillsmatter.com/">SkillsMatter</a>.</p>
<p>Here&rsquo;s a list of questions I had the chance to ask Hans:</p>
<ul>
<li>What&rsquo;s Gradle?</li>
<li>Any new about Gradle 1.0 and what&rsquo;s being worked on?</li>
<li>Why is Gradle using Groovy for its DSL?</li>
<li>Is it possible to enforce conventions and standards à la Maven?</li>
<li>Some words on IDE integration, roadmap, etc?</li>
</ul>
<p>Check the <a href="http://skillsmatter.com/podcast/home/interview-on-gradle/js-3210">video</a>!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Tweets sympas sur Free Mobile</title><link>https://glaforge.dev/posts/2012/01/10/tweets-sympas-sur-free-mobile/</link><pubDate>Tue, 10 Jan 2012 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2012/01/10/tweets-sympas-sur-free-mobile/</guid><description>&lt;p>Xavier Niel a annoncé en fanfare l&amp;rsquo;arrivée de &lt;a href="http://mobile.free.fr/index.html">Free Mobile&lt;/a> après un long suspense et un buzz savamment orchestré. Et j&amp;rsquo;ai retenu quelques tweets qui m&amp;rsquo;ont bien fait rire :&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/#!/Nanaki43/status/156754481328033793">Enfin on peut dire t&amp;rsquo;as un forfait à 2 balles mon gars&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/#!/Assia_H/status/156745335186792449">Quand les mouettes changent d&amp;rsquo;opérateur, c&amp;rsquo;est qu&amp;rsquo;elles pensent qu&amp;rsquo;elles sont devenues des pigeons #Cantona&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/#!/75_Scientifique/status/156740776703959041">Je ne regarderais plus jamais une pièce de 2€ de la même manière&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/#!/TilltheCat/status/156653694312644608">#Freemobile 19,99€ en tout illimité. A ce prix là, on va enfin pouvoir se faire 5 #Free et légumes par jour.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/#!/ChristopheGen/status/156659272422408192">#Free vient de faire plus pour le pouvoir d&amp;rsquo;achat en 5 minutes que Nicolas #Sarkozy en 5 ans&amp;hellip;&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/#!/Thinkit/status/156720751502835712">Au resto à midi on a failli laisse un pourboire de 2€. Puis on s&amp;rsquo;est dit que c&amp;rsquo;était quand même un mois de forfait&amp;hellip;&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>On verra si Orange se presse de réagir ou s&amp;rsquo;il voit rouge, si Bouygues bétonne ses offres ou si SFR va se laisser faire&amp;hellip;&lt;/p></description><content:encoded>
<![CDATA[<p>Xavier Niel a annoncé en fanfare l&rsquo;arrivée de <a href="http://mobile.free.fr/index.html">Free Mobile</a> après un long suspense et un buzz savamment orchestré. Et j&rsquo;ai retenu quelques tweets qui m&rsquo;ont bien fait rire :</p>
<ul>
<li><a href="https://twitter.com/#!/Nanaki43/status/156754481328033793">Enfin on peut dire t&rsquo;as un forfait à 2 balles mon gars</a></li>
<li><a href="https://twitter.com/#!/Assia_H/status/156745335186792449">Quand les mouettes changent d&rsquo;opérateur, c&rsquo;est qu&rsquo;elles pensent qu&rsquo;elles sont devenues des pigeons #Cantona</a></li>
<li><a href="https://twitter.com/#!/75_Scientifique/status/156740776703959041">Je ne regarderais plus jamais une pièce de 2€ de la même manière</a></li>
<li><a href="https://twitter.com/#!/TilltheCat/status/156653694312644608">#Freemobile 19,99€ en tout illimité. A ce prix là, on va enfin pouvoir se faire 5 #Free et légumes par jour.</a></li>
<li><a href="https://twitter.com/#!/ChristopheGen/status/156659272422408192">#Free vient de faire plus pour le pouvoir d&rsquo;achat en 5 minutes que Nicolas #Sarkozy en 5 ans&hellip;</a></li>
<li><a href="https://twitter.com/#!/Thinkit/status/156720751502835712">Au resto à midi on a failli laisse un pourboire de 2€. Puis on s&rsquo;est dit que c&rsquo;était quand même un mois de forfait&hellip;</a></li>
</ul>
<p>On verra si Orange se presse de réagir ou s&rsquo;il voit rouge, si Bouygues bétonne ses offres ou si SFR va se laisser faire&hellip;</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Caelyf 0.1, a lightweight Groovy toolkit for Cloud Foundry</title><link>https://glaforge.dev/posts/2011/12/23/caelyf-0-1-a-lightweight-groovy-toolkit-for-cloud-foundry/</link><pubDate>Fri, 23 Dec 2011 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2011/12/23/caelyf-0-1-a-lightweight-groovy-toolkit-for-cloud-foundry/</guid><description>&lt;p>I&amp;rsquo;m very happy to announce the release of &lt;a href="http://caelyf.cloudfoundry.com">Caelyf&lt;/a> 0.1, a lightweight Groovy toolkit for Cloud Foundry.&lt;/p>
&lt;p>Caelyf is inspired by the &lt;a href="http://gaelyk.appspot.com">Gaelyk&lt;/a> project, and actually borrows most of its code base. Its goal is to let you write apps using Groovy scripts and templates, and then deploy them on the Cloud Foundry platform.&lt;/p>
&lt;p>Caelyf is Open Source, released under the Apache 2 license.&lt;/p>
&lt;p>Its source code is hosted on GitHub: &lt;a href="https://github.com/glaforge/caelyf">https://github.com/glaforge/caelyf&lt;/a> Which means you will also be able to use the issue tracker from GitHub to file bugs or feature requests. And of course, if you want to contribute to the project, you&amp;rsquo;re just one pull request away!&lt;/p></description><content:encoded>
<![CDATA[<p>I&rsquo;m very happy to announce the release of <a href="http://caelyf.cloudfoundry.com">Caelyf</a> 0.1, a lightweight Groovy toolkit for Cloud Foundry.</p>
<p>Caelyf is inspired by the <a href="http://gaelyk.appspot.com">Gaelyk</a> project, and actually borrows most of its code base. Its goal is to let you write apps using Groovy scripts and templates, and then deploy them on the Cloud Foundry platform.</p>
<p>Caelyf is Open Source, released under the Apache 2 license.</p>
<p>Its source code is hosted on GitHub: <a href="https://github.com/glaforge/caelyf">https://github.com/glaforge/caelyf</a> Which means you will also be able to use the issue tracker from GitHub to file bugs or feature requests. And of course, if you want to contribute to the project, you&rsquo;re just one pull request away!</p>
<p>We&rsquo;re using <a href="https://groups.google.com/group/caelyf">Google groups</a> for interacting with our upcoming users.</p>
<p>Caelyf is obviously very young (ie. that&rsquo;s its first release), so it might be rough around the edges, and I&rsquo;d appreciate if you provide me with some feedback as you delve into the project and deploy your first applications.</p>
<p>As a proof of concept, the first application written with Caelyf is the Caelyf website itself: <a href="http://caelyf.cloudfoundry.com">http://caelyf.cloudfoundry.com</a> It&rsquo;s always great to be able to eat one&rsquo;s own dog food!</p>
<p>On the <a href="http://caelyf.cloudfoundry.com">Caelyf website</a>, you&rsquo;ll find a <a href="http://caelyf.cloudfoundry.com/tutorial">detailed tutorial</a> explaining what you can do with the framework, and how to use it. I haven&rsquo;t included the steps to get setup on Cloud Foundry, but you&rsquo;ll find the link to the Cloud Foundry documentation for that purpose.</p>
<p>You will also find the links for downloading a template project, or just a JAR if you prefer that: <a href="http://caelyf.cloudfoundry.com/download">http://caelyf.cloudfoundry.com/download</a></p>
<p>Well, that&rsquo;s pretty much it, so, have fun, tell me what your experience is, and Merry Christmas :-)</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 1.8.5 and second beta of 2.0 out</title><link>https://glaforge.dev/posts/2011/12/23/groovy-1-8-5-and-second-beta-of-2-0-out/</link><pubDate>Fri, 23 Dec 2011 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2011/12/23/groovy-1-8-5-and-second-beta-of-2-0-out/</guid><description>&lt;p>The Groovy development team has just announced a joint release of Groovy 1.8.5 and a second beta of the upcoming 2.0 release.&lt;br />
We&amp;rsquo;d be happy to hear your feedback on the static type checking support and the experimental static compilation as well.&lt;br />
Here&amp;rsquo;s the &lt;a href="http://docs.codehaus.org/display/GROOVY/2011/12/23/Groovy+1.8.5+and+2.0-beta-2+released">official announcement&lt;/a>:&lt;/p>
&lt;blockquote>
&lt;p>The Groovy development team is happy to deliver two new releases as early Christmas presents: Groovy 1.8.5 and 2.0-beta-2.&lt;/p>
&lt;p>While we work hard on Invoke Dynamic support, Static Type Checking and some experimental static compilation, we also find time to fix a good load of bugs and some minor improvements, as those two releases here are essentially minor incremental versions.&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy development team has just announced a joint release of Groovy 1.8.5 and a second beta of the upcoming 2.0 release.<br />
We&rsquo;d be happy to hear your feedback on the static type checking support and the experimental static compilation as well.<br />
Here&rsquo;s the <a href="http://docs.codehaus.org/display/GROOVY/2011/12/23/Groovy+1.8.5+and+2.0-beta-2+released">official announcement</a>:</p>
<blockquote>
<p>The Groovy development team is happy to deliver two new releases as early Christmas presents: Groovy 1.8.5 and 2.0-beta-2.</p>
<p>While we work hard on Invoke Dynamic support, Static Type Checking and some experimental static compilation, we also find time to fix a good load of bugs and some minor improvements, as those two releases here are essentially minor incremental versions.</p>
<p>You can have a look at the tickets closed here, for 1.8.5:<br />
<a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=18071">http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=18071</a></p>
<p>And here for 2.0-beta-2:<br />
<a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=18072">http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=18072</a></p>
<p>You can download both distributions at the usual place:<br />
<a href="http://groovy.codehaus.org/Download?nc">http://groovy.codehaus.org/Download?nc</a></p>
<p>If you want to play with the experimental static compilation, you can do so by following the instructions from the GEP-10 page:<br />
<a href="http://docs.codehaus.org/display/GroovyJSR/GEP+10+-+Static+compilation">http://docs.codehaus.org/display/GroovyJSR/GEP+10+-+Static+compilation</a> Look for the &ldquo;testing&rdquo; paragraph in the middle of the document, which contains a link to the experimental JAR, as well as some basic explanations.</p>
<p>Thanks a lot to all those who contributed to this release: developers, contributors, users, bug reporters.</p>
<p>On behalf of the Groovy development team, we&rsquo;d like to wish you a Groovy Christmas and a very Happy New Year.</p></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Videos and slides of Groovy / Grails eXchange 2011</title><link>https://glaforge.dev/posts/2011/12/17/videos-and-slides-of-groovy-grails-exchange-2011/</link><pubDate>Sat, 17 Dec 2011 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2011/12/17/videos-and-slides-of-groovy-grails-exchange-2011/</guid><description>&lt;p>A week ago ended the latest edition of the &lt;a href="http://skillsmatter.com/event/groovy-grails/groovy-grails-exchange-2011/js-3132">Groovy &amp;amp; Grails eXchange 2011&lt;/a> conference in London, organized by the fine and lovely folks of &lt;a href="http://skillsmatter.com/">SkillsMatter&lt;/a>.&lt;/p>
&lt;p>The organizers have a great setup which allows them to record and publish all the presentations almost live! And I&amp;rsquo;d like in this post to give you a link to each and every presentation that was given during the conference:&lt;/p>
&lt;p>First day:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://skillsmatter.com/podcast/groovy-grails/talk-by-guillaume-laforge-2475">Groovy 1.8, 2.0 and beyond&lt;/a> by Guillaume Laforge&lt;/li>
&lt;li>&lt;a href="http://skillsmatter.com/podcast/groovy-grails/groovy-android">Discobot: Groovy on Android&lt;/a> by Erik Pragt and Marcin Erdmann&lt;/li>
&lt;li>&lt;a href="http://skillsmatter.com/podcast/groovy-grails/gaelyk">A Gaelyk update&lt;/a> by Guillaume Laforge&lt;/li>
&lt;li>&lt;a href="http://skillsmatter.com/podcast/groovy-grails/effective-groovy">Getting Groovy on the web and desktop&lt;/a> by Andres Almiray&lt;/li>
&lt;li>&lt;a href="http://skillsmatter.com/podcast/groovy-grails/enterprise-gradle">Enterprise Gradle&lt;/a> by Hans Dokter&lt;/li>
&lt;li>&lt;a href="http://skillsmatter.com/podcast/groovy-grails/advanced-gradle">Advanced Gradle&lt;/a> by Hans Dokter&lt;/li>
&lt;li>&lt;a href="http://skillsmatter.com/podcast/groovy-grails/griffon">Painless desktop application development with Griffon&lt;/a> by Andres Almiray&lt;/li>
&lt;li>&lt;a href="http://skillsmatter.com/podcast/groovy-grails/spock">Smarter testing with Spock&lt;/a> by Luke Daley&lt;/li>
&lt;li>&lt;a href="http://skillsmatter.com/podcast/groovy-grails/groovy-vert-x">Asynchronous IO on the web with vert.x&lt;/a> by Peter Ledbrook&lt;/li>
&lt;li>&lt;a href="http://skillsmatter.com/podcast/groovy-grails/gpars">Just keep passing the messages with GPars&lt;/a> by Russel Winder&lt;/li>
&lt;li>&lt;a href="http://skillsmatter.com/podcast/groovy-grails/metaprogramming-groovy">Powerful metaprogramming techniques with Groovy&lt;/a> by Jeff Brown&lt;/li>
&lt;/ul>
&lt;p>Second day:&lt;/p></description><content:encoded>
<![CDATA[<p>A week ago ended the latest edition of the <a href="http://skillsmatter.com/event/groovy-grails/groovy-grails-exchange-2011/js-3132">Groovy &amp; Grails eXchange 2011</a> conference in London, organized by the fine and lovely folks of <a href="http://skillsmatter.com/">SkillsMatter</a>.</p>
<p>The organizers have a great setup which allows them to record and publish all the presentations almost live! And I&rsquo;d like in this post to give you a link to each and every presentation that was given during the conference:</p>
<p>First day:</p>
<ul>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/talk-by-guillaume-laforge-2475">Groovy 1.8, 2.0 and beyond</a> by Guillaume Laforge</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/groovy-android">Discobot: Groovy on Android</a> by Erik Pragt and Marcin Erdmann</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/gaelyk">A Gaelyk update</a> by Guillaume Laforge</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/effective-groovy">Getting Groovy on the web and desktop</a> by Andres Almiray</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/enterprise-gradle">Enterprise Gradle</a> by Hans Dokter</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/advanced-gradle">Advanced Gradle</a> by Hans Dokter</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/griffon">Painless desktop application development with Griffon</a> by Andres Almiray</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/spock">Smarter testing with Spock</a> by Luke Daley</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/groovy-vert-x">Asynchronous IO on the web with vert.x</a> by Peter Ledbrook</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/gpars">Just keep passing the messages with GPars</a> by Russel Winder</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/metaprogramming-groovy">Powerful metaprogramming techniques with Groovy</a> by Jeff Brown</li>
</ul>
<p>Second day:</p>
<ul>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/grails-2-0">Better productivity with Grails 2.0</a> by Peter Ledbrook</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/using-weceem-an-embeddable-grails-cms">Using Weceem, an embeddable CMS</a> by Marc Palmer</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/hacking-the-spring-security-plugin">Hacking the Grails Spring Security plugin</a> by Burt Beckwith</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/tackling-day-to-day-grails-decisions">Tackling day to day Grails decisions</a> by Andreas Arledal</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/grails-data-mapping-making-your-nosql-database-gorm-compliant">Grails data mapping, make your NoSQL store GORM compliant</a> by Stefan Armbruster</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/grails-as-mobile-backend">A year in the life of a Grails startup</a> by Tomas Lin and Donovan Frew</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/testing-http-dependencies-with-betamax">Testing HTTP dependencies with BetaMax</a> by Robert Fletcher</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/continuous-delivery-in-the-cloud-with-cloudbees">Continuous delivery in the cloud with CloudBees</a> by Marco Vermeulen and Nicolas de Loof</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/productive-grails-functional-testing">Productive Grails functional testing</a> by Luke Daley</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/grails-cloud-foundry">A cloud for developers: Cloud Foundry</a> by Peter Ledbrook</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/talk-to-be-confirmed-2810">Maintaining test suites with Groovy and Twist</a> by Andrew Kemp</li>
<li><a href="http://skillsmatter.com/podcast/groovy-grails/keeping-grails-one-step-ahead">Keeping Grails one step ahead</a> by Marc Palmer</li>
</ul>
<p>And there&rsquo;s also my little <a href="http://skillsmatter.com/podcast/groovy-grails/interview-on-gradle">interview of Hans Dokter about Gradle</a>!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>InfoQ interview: Latest Happenings and Future of Groovy</title><link>https://glaforge.dev/posts/2011/11/30/infoq-interview-latest-happenings-and-future-of-groovy/</link><pubDate>Wed, 30 Nov 2011 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2011/11/30/infoq-interview-latest-happenings-and-future-of-groovy/</guid><description>&lt;p>I had the pleasure of being interviewed by Rick Hightower for InfoQ, on the features of Groovy 1.8, and also what we&amp;rsquo;re working on for Groovy 2.0.&lt;/p>
&lt;p>If you&amp;rsquo;re interested, here&amp;rsquo;s the article: &lt;a href="http://www.infoq.com/news/2011/11/groovy-updates">Latest Happenings and Future of Groovy 1.8, 2.0 and Beyond&lt;/a>&lt;/p></description><content:encoded>
<![CDATA[<p>I had the pleasure of being interviewed by Rick Hightower for InfoQ, on the features of Groovy 1.8, and also what we&rsquo;re working on for Groovy 2.0.</p>
<p>If you&rsquo;re interested, here&rsquo;s the article: <a href="http://www.infoq.com/news/2011/11/groovy-updates">Latest Happenings and Future of Groovy 1.8, 2.0 and Beyond</a></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Handy Gradle startup script</title><link>https://glaforge.dev/posts/2011/11/25/handy-gradle-startup-script/</link><pubDate>Fri, 25 Nov 2011 05:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2011/11/25/handy-gradle-startup-script/</guid><description>&lt;p>&lt;a href="https://twitter.com/mittie">Dierk&lt;/a> published a &lt;a href="https://gist.github.com/1271516">gist&lt;/a> on GitHub with a handy Gradle build script to help you bootstrap a Gradle-built project from scratch, without having to create the directory layout manually, or install the Gradle wrapper.&lt;/p>
&lt;p>This is pretty neat and should be integrated in Gradle to ease the creation of projects!&lt;/p>
&lt;p>I&amp;rsquo;ve &lt;a href="https://gist.github.com/1393868">updated the gist&lt;/a> with a more recent version of Groovy and Gradle.&lt;/p>
&lt;p>And so that I never forget about this handy Gradle build script, I&amp;rsquo;m blogging about it and reproducing it here, to save me precious minutes finding it again the next time I need it! So without further ado, here&amp;rsquo;s the script in question:&lt;/p></description><content:encoded>
<![CDATA[<p><a href="https://twitter.com/mittie">Dierk</a> published a <a href="https://gist.github.com/1271516">gist</a> on GitHub with a handy Gradle build script to help you bootstrap a Gradle-built project from scratch, without having to create the directory layout manually, or install the Gradle wrapper.</p>
<p>This is pretty neat and should be integrated in Gradle to ease the creation of projects!</p>
<p>I&rsquo;ve <a href="https://gist.github.com/1393868">updated the gist</a> with a more recent version of Groovy and Gradle.</p>
<p>And so that I never forget about this handy Gradle build script, I&rsquo;m blogging about it and reproducing it here, to save me precious minutes finding it again the next time I need it! So without further ado, here&rsquo;s the script in question:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>apply plugin <span style="color:#666">:</span> <span style="color:#4070a0">&#39;groovy&#39;</span>
</span></span><span style="display:flex;"><span>apply plugin <span style="color:#666">:</span> <span style="color:#4070a0">&#39;idea&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>repositories <span style="color:#666">{</span> mavenCentral<span style="color:#666">()</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>dependencies <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    groovy <span style="color:#4070a0">&#39;org.codehaus.groovy:groovy-all:1.8.4&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>task <span style="color:#06287e">makeDirs</span><span style="color:#666">(</span>description <span style="color:#666">:</span> <span style="color:#4070a0">&#39;make all dirs for project setup&#39;</span><span style="color:#666">)</span> <span style="color:#666">&lt;&lt;</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> sources <span style="color:#666">=</span> <span style="color:#666">[</span>sourceSets<span style="color:#666">.</span><span style="color:#4070a0">main</span><span style="color:#666">,</span> sourceSets<span style="color:#666">.</span><span style="color:#4070a0">test</span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>    sources<span style="color:#666">*.</span><span style="color:#4070a0">allSource</span><span style="">\</span><span style="color:#666">.</span><span style="color:#4070a0">srcDirs</span><span style="color:#666">.</span><span style="color:#4070a0">flatten</span><span style="color:#666">().</span><span style="color:#4070a0">each</span> <span style="color:#666">{</span> File srcDir <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>        println <span style="color:#4070a0">&#34;making $srcDir&#34;</span>
</span></span><span style="display:flex;"><span>        srcDir<span style="color:#666">.</span><span style="color:#4070a0">mkdirs</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>task <span style="color:#06287e">wrap</span><span style="color:#666">(</span>type <span style="color:#666">:</span> Wrapper<span style="color:#666">,</span> description <span style="color:#666">:</span> <span style="color:#4070a0">&#34;create a gradlew&#34;</span><span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    gradleVersion <span style="color:#666">=</span> <span style="color:#4070a0">&#39;1.0-milestone-6&#39;</span>  <span style="color:#666">}</span>
</span></span></code></pre></div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Coming back to the new Google App Engine pricing policy</title><link>https://glaforge.dev/posts/2011/11/25/coming-back-to-the-new-google-app-engine-pricing-policy/</link><pubDate>Fri, 25 Nov 2011 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2011/11/25/coming-back-to-the-new-google-app-engine-pricing-policy/</guid><description>&lt;p>In a recent article, I was &lt;a href="https://glaforge.dev/posts/2011/09/01/google-app-engine-s-new-pricing-model/">complaining about the new Google App Engine pricing policy&lt;/a>. Obviously, as I have a few applications deployed on App Engine, and as I&amp;rsquo;m developing &lt;a href="http://gaelyk.appspot.com">Gaelyk&lt;/a>, a lightweight toolkit for this platform, I was worried about being heavily affected by those changes.&lt;/p>
&lt;p>In this article, I&amp;rsquo;d like to do a short summary of my experience so far.&lt;/p>
&lt;p>I have close to 10 applications deployed on Google App Engine. Most are just demos that nobody ever accesses. But three of them are quite important to me:&lt;/p></description><content:encoded>
<![CDATA[<p>In a recent article, I was <a href="https://glaforge.dev/posts/2011/09/01/google-app-engine-s-new-pricing-model/">complaining about the new Google App Engine pricing policy</a>. Obviously, as I have a few applications deployed on App Engine, and as I&rsquo;m developing <a href="http://gaelyk.appspot.com">Gaelyk</a>, a lightweight toolkit for this platform, I was worried about being heavily affected by those changes.</p>
<p>In this article, I&rsquo;d like to do a short summary of my experience so far.</p>
<p>I have close to 10 applications deployed on Google App Engine. Most are just demos that nobody ever accesses. But three of them are quite important to me:</p>
<ul>
<li><a href="http://gaelyk.appspot.com">the Gaelyk website</a></li>
<li><a href="https://glaforge.dev/">my blog</a></li>
<li><a href="http://groovyconsole.appspot.com">the Groovy Web Console</a></li>
</ul>
<p>The two key aspects that are bothersome are: the instance hours and the datastore reads. That&rsquo;s usually those two limits that we tend to hit — at least, that was the case for me.</p>
<p>Despite the policy changes, the Gaelyk website wasn&rsquo;t affected, and was staying under the quotas. It&rsquo;s essentially a pretty static website, although the pages are templates (hence dynamic), but since the pages are cached, through the <a href="http://gaelyk.appspot.com/tutorial/url-routing#caching">routes.groovy configuration</a>, and that there&rsquo;s no datastore access, everything is fine here — just a few datastore reads for the memcache access.</p>
<p>For my blog, I was a bit below the datastore reads level, but the frontend instance hours were more problematic. The fact of moving the max idle instance knobs and latency knobs to their opposite, helped me a lot. And although the website is up and running 24h/24, App Engine doesn&rsquo;t spin up too many instances anymore with those knob settings, and I&rsquo;m now under the 28 hours limit. So all is fine for my blog too.</p>
<p>And the biggest problem was the Groovy Web Console&hellip;</p>
<p>For the frontend instance hours, I&rsquo;m always at the limit, but with the knobs to the opposite, it&rsquo;s okay, but really short&hellip; Also, the pricing policy moving to instance hours rather than CPU consumption was a good thing for the application, as users can execute long-running scripts (&lt; 60 seconds).</p>
<p>For the datastore reads, that was the most painful aspect, as the Google Search bots and friends are doing a ton of reads when harvesting all the Groovy scripts that are published.</p>
<p>I must confess the application wasn&rsquo;t very well written, so for example, I was doing three queries (basically the same one) to list the latest scripts published, the scripts by authors, the scripts by tags. Three queries which were essentially the same, more or less, that were totalling 5 read operations each (on op for the query + 2 * two ops because of the indexed columns I was sorting / filtering on). So I had like 15 operations or so just to build up the pages that you see when you click on the link &ldquo;recent scripts&rdquo;.</p>
<p>Despite the fact my pages were cached (using just one read operation from memcache), it happened often that pages were not in the cache, and that my app needed those 15 operations. So I had to optimize my application. Instead of doing three different queries, I ended up doing just one, and then I apply some sorting / filtering / grouping in memory with the results I got from the query. I ended up with just 5 operations instead of 15.</p>
<p>Then, I also went a step further by putting the results of the queries in memcache, so instead of 5 ops, most of the time I ended up getting only one read operation. In the end, I reduced my datastore reads by a factor of 15 or so. And with those changes, I only consume about 40% of the datastore read operations quota!</p>
<p>You can see what my quotas look like when I&rsquo;m at the end of the day, thanks to those optimizations:</p>
<p><figure>
  <a href="#img-5a2075a71d18aab771b6c6b82c97b3c6">
    <img src="/img/misc/gae-console-billing.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-5a2075a71d18aab771b6c6b82c97b3c6">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/gae-console-billing.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I almost reach the frontend instance hours limit, but I&rsquo;m still below 28 hours, and I only used 38% of my datastore read quota.</p>
<p>So all in all, I&rsquo;m quite happy that all my apps stayed free, thanks to the few updates that Google made to the pricing policy to accomodate all the complaints they received!</p>
<p>What has been your experience with the new pricing policy? Did you have to optimize your apps, if yes, what solutions did you find?</p>
<p>I&rsquo;m looking forward to your feedback!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Gaelyk 1.1 released</title><link>https://glaforge.dev/posts/2011/11/16/gaelyk-1-1-released/</link><pubDate>Wed, 16 Nov 2011 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2011/11/16/gaelyk-1-1-released/</guid><description>&lt;p>Gaelyk 1.1 has just been released!&lt;br />
&lt;a href="http://gaelyk.appspot.com">Gaelyk&lt;/a> is a lightweight toolkit for writing and deploying Groovy apps on Google App Engine.&lt;br />
In this version, all the components have been updated to their latest versions:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://glaforge.dev/posts/2011/11/11/latest-groovy-releases-and-roadmap-update/">Groovy 1.8.4&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://googleappengine.blogspot.com/2011/11/app-engine-160-out-of-preview-release.html">GAE SDK 1.6.0&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>This blog is now running Gaelyk 1.1 pretty happily!&lt;br />
You should be able to see the &lt;a href="http://groups.google.com/group/gaelyk/browse_thread/thread/b91556a6c2238646">announcement&lt;/a> on the Gaelyk Google group for the details, but here&amp;rsquo;s the list of changes:&lt;/p></description><content:encoded>
<![CDATA[<p>Gaelyk 1.1 has just been released!<br />
<a href="http://gaelyk.appspot.com">Gaelyk</a> is a lightweight toolkit for writing and deploying Groovy apps on Google App Engine.<br />
In this version, all the components have been updated to their latest versions:</p>
<ul>
<li><a href="https://glaforge.dev/posts/2011/11/11/latest-groovy-releases-and-roadmap-update/">Groovy 1.8.4</a></li>
<li><a href="http://googleappengine.blogspot.com/2011/11/app-engine-160-out-of-preview-release.html">GAE SDK 1.6.0</a></li>
</ul>
<p>This blog is now running Gaelyk 1.1 pretty happily!<br />
You should be able to see the <a href="http://groups.google.com/group/gaelyk/browse_thread/thread/b91556a6c2238646">announcement</a> on the Gaelyk Google group for the details, but here&rsquo;s the list of changes:</p>
<blockquote>
<ul>
<li>
<p>Upgraded to Groovy 1.8.4 and App Engine SDK 1.6.0</p>
</li>
<li>
<p>The <a href="http://gaelyk.appspot.com/tutorial/app-engine-shortcuts#datastore-get">new <code>get()</code> methods on the datastore service</a> now also work with the asynchronous datastore.</p>
</li>
<li>
<p>Added an <code>unindexed</code> property on entities to set unindexed properties: <code>person.unindexed.bio = &quot;...&quot;</code></p>
</li>
<li>
<p><a href="http://gaelyk.appspot.com/tutorial/app-engine-shortcuts#pogo-entity-coercion-annotations">Three annotations to customize the bean / entity coercion</a>
(<code>@Key</code>, <code>@Unindexed</code> and <code>@Ignore</code>)</p>
</li>
<li>
<p>Part of the work with the async datastore <code>get()</code>, whenever you have a <code>Future</code> to deal with, for example when the async datastore returns a <code>Future</code> , you can call any property on the <code>Future</code> object, and it will proxy those property access to the underlying object returned by the <code>get()</code> call on the future.</p>
</li>
<li>
<p>In addition to <code>datastore.query{}</code> and <code>datastore.execute{}</code>, there is now a <code>datastore.iterate{}</code> method that returns an <code>Iterator</code> instead of a list, which is friendlier when your queries return a large number of results.</p>
</li>
<li>
<p>Added the prospective search service to the binding</p>
</li>
<li>
<p>You can access the <a href="http://gaelyk.appspot.com/tutorial/app-engine-shortcuts#async-memcache">asynchronous Memcache</a> service with <code>memcache.async</code></p>
</li>
<li>
<p>Additional <a href="http://gaelyk.appspot.com/tutorial/app-engine-shortcuts#files-misc">convenience methods for the file service</a></p>
</li>
<li>
<p>Added an <a href="http://gaelyk.appspot.com/tutorial/app-engine-shortcuts#blobstore-each-collect">each and collect method on blobstore</a> to iterate over all the blobs from the blobstore, or to collect some values from all blob infos stored.</p>
</li>
</ul></blockquote>
<p>I&rsquo;ll just highlight one of the nice little additions mentioned above, which is the refinement of the POGO / Entity coercion mechanism. You can now use three annotations to further customize that conversion: @Key, @Unindexed and @Ignore, to respectively specify the key property, properties that should not be indexed by the datastore, and properties that should be ignored and not saved in the datastore.<br />
Here&rsquo;s what a class annotated with those annotations could look like:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Canonical</span>  
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Person</span> <span style="color:#666">{</span>  
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Key</span> String login  
</span></span><span style="display:flex;"><span>    String firstName  
</span></span><span style="display:flex;"><span>    String lastName  
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Unindexed</span> String bio  
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Ignore</span> String getFullName<span style="color:#666">()</span> <span style="color:#666">{</span> <span style="color:#4070a0">&#34;$firstName $lastName&#34;</span> <span style="color:#666">}</span>   
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>And then you can use the as operator to convert between beans and entities.<br />
If you&rsquo;ve read thus far, it&rsquo;s time to go <a href="http://gaelyk.appspot.com/download">download Gaelyk</a> and have fun!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Latest Groovy releases and roadmap updates</title><link>https://glaforge.dev/posts/2011/11/11/latest-groovy-releases-and-roadmap-update/</link><pubDate>Fri, 11 Nov 2011 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2011/11/11/latest-groovy-releases-and-roadmap-update/</guid><description>&lt;p>On &lt;a href="http://docs.codehaus.org/pages/viewpage.action?pageId=227053189">this post on the Groovy website&lt;/a>, we&amp;rsquo;ve announced the releases of Groovy 1.8.4 and the first beta of 2.0, as well as cover some updates on the roadmap.&lt;/p>
&lt;p>In a nutshell, the big highlights are the static type checking support and invoke dynamic support. We&amp;rsquo;re also going to investigate whether it makes sense to also cover static compilation. And we&amp;rsquo;ve also announced a new version numbering scheme, to move Groovy forward.&lt;/p></description><content:encoded>
<![CDATA[<p>On <a href="http://docs.codehaus.org/pages/viewpage.action?pageId=227053189">this post on the Groovy website</a>, we&rsquo;ve announced the releases of Groovy 1.8.4 and the first beta of 2.0, as well as cover some updates on the roadmap.</p>
<p>In a nutshell, the big highlights are the static type checking support and invoke dynamic support. We&rsquo;re also going to investigate whether it makes sense to also cover static compilation. And we&rsquo;ve also announced a new version numbering scheme, to move Groovy forward.</p>
<p>Here&rsquo;s what the announcement said:</p>
<blockquote>
<p>It is with great pleasure that we are announcing another joint release today!</p>
<p>We have <strong>released Groovy 1.8.4</strong>, that includes fixes and minor improvements to correct problems the Grails web framework and the Gradle build automation tool were having with 1.8.3.</p>
<p>In addition, we are also pleased to announce the <strong>release of Groovy 2.0-beta-1!</strong> <strong>Groovy&rsquo;s updated version numbering scheme</strong>  Yes, you heard it, a Groovy 2.0 beta! But what&rsquo;s going on, weren&rsquo;t we working on the 1.9 line? Yes, we were, but with the substantial number of new features coming up in the 1.9 release, we thought it was time to start using an updated numbering scheme.<br />
Historically, Groovy&rsquo;s &ldquo;major&rdquo; versions have always been &ldquo;dot&rdquo; versions, which is a bit uncommon in our industry, in the sense that major versions are expected to have a new major digit each time. So the Groovy version numbering scheme has always been a bit odd for strangers and newcomers &ndash; furthermore, from a marketing perspective, if we ever decide to release a 1.10 version, it would be lexicographically backward (1.10 &lt; 1.9 in math)! This isn&rsquo;t the first time a major change in version numbers has happened: Groovy went from 1.1 to 1.5 and recently Grails changed from 1.4 to 2.0 for its next major release.</p>
<p>So instead of speaking of the mythical &ldquo;2.0&rdquo; version of Groovy that never seems to arrive, this next major release will be 2.0. For further versions, we&rsquo;ll probably release some 2.x versions as well, but we will also adopt the same strategy regarding major version numbers, releasing a major version more or less every year. Thus, given <strong>2.0 final should be out in early 2012</strong>, the following major version will be released afterwards in 2013 and will become 3.0. But don&rsquo;t worry, we won&rsquo;t adopt the lightning fast numbering schemes of Google Chrome or Mozilla Firefox!</p>
<p><strong>So what will be inside 2.0 that makes it so special?</strong> <strong>Static type checking</strong> We have been working on the <strong>static type checking support for Groovy</strong>.</p>
<p>On several occasions, we have noticed that Groovy was used in Java projects as a simple yet powerful scripting language for Java APIs, in ways that don&rsquo;t particularly leverage the dynamic aspects of the language but instead as a better syntax for Java somehow. With this approach, developers often prefer a stricter approach to type checking, in the sense it should complain when you have typos in your method names, parameters or variables or when you make invalid assignments, etc. The compiler should complain at compile time, rather than having Groovy complain at execution time later on, thus giving you earlier feedback about those errors — particularly useful when you are using APIs that may change.</p>
<p>Static type checking has already been discussed on a few threads on the Groovy mailing-lists, and we appreciate your feedback on how it&rsquo;s being implemented, how it should behave, and so on. Please don&rsquo;t hesitate to come back to us with your thoughts and ideas on this topic.</p>
<p>We have also created a GEP (Groovy Extension Proposal) covering this new aspect:<br />
<a href="http://docs.codehaus.org/display/GroovyJSR/GEP+8+-+Static+type+checking">http://docs.codehaus.org/display/GroovyJSR/GEP+8+-+Static+type+checking</a> And Cédric, who joined Jochen and myself at SpringSource/VMware and who&rsquo;s been working on this feature, wrote a very nice and detailed blog post about the current work-in-progress here:<a href="http://www.jroller.com/melix/entry/groovy_static_type_checker_status">http://www.jroller.com/melix/entry/groovy_static_type_checker_status</a><br />
Be sure to read those two documents, as they will guide you through some interesting samples showing how you can get started using static type checking with Groovy 2.0.0-beta-1! Please also note that this is still in beta, and that APIs are subject to change and evolve depending on your feedback.</p>
<p><strong>Static compilation</strong> With static type checking, type inference capabilities, sensitive flow typing, and so on, the Groovy compiler is now way smarter in figuring out what your code is actually doing. In the case of scripting Java APIs that we mentioned above, we could let the compiler generate direct method calls à la Java, instead of going through Groovy&rsquo;s Meta-Object Protocol, allowing <strong>the same performance as Java</strong>!<br />
We&rsquo;re going to investigate <strong>static compilation</strong> in 2.0, leveraging the new smarts of the compiler and its infrastructure, by creating a new git branch with initial support that you&rsquo;ll be able to monitor to watch our progress on that front.</p>
<p><strong>But what about Groovy++?</strong> Thanks to the great work of Alex Tkachman and Roshan Dawrani, the Groovy++ extension project definitely inspired us and ended up convincing us that it was important to support static type checking and compilation in Groovy.</p>
<p>We couldn&rsquo;t simply integrate Groovy++ directly in Groovy 2.0, as it differed in spirit from our ideas for Groovy core, for example by covering more ground than we wanted and needed (persistent collections, new operators, traits, new map-style classes, etc.). We also wanted to evolve the Groovy compiler infrastructure so that it fits nicely with the primitive optimizations work, static type checking and compilation, as well as invoke dynamic support, and we would have needed to refactor Groovy++ heavily for our needs. That said, we of course would welcome collaboration with the Groovy++ team to learn from their experiences building Groovy++.</p>
<p>We engaged the community to discuss various aspects of static type checking and inference so as to make it closer to the usual Groovy semantics as much as possible, without introducing new data structures or restricting existing features (like closures with non-final variables), so that Groovy developers feel at ease with static type checking and compilation, and are the least impacted by differences in semantics between this additional mode and the classical dynamic mode.</p>
<p><strong>Invoke dynamic support</strong> Lastly, we&rsquo;ve started working on <strong>&ldquo;invoke dynamic&rdquo; support</strong>, the new JVM bytecode instruction and APIs of JDK 7 developed for dynamic languages, with the goal of <strong>improving the performance of all the dynamic aspects of Groovy in significant ways</strong>. This is still early days, and this first beta of 2.0 doesn&rsquo;t yet contain particular changes that you&rsquo;d notice. We are working on the &ldquo;indy&rdquo; branch of Git (and mirrored on GitHub) for that support.</p>
<p><strong>And beyond 2.0?</strong> With static type checking and compilation, as well as invoke dynamic support, we thought that it was really worth a Groovy 2.0 moniker! But what&rsquo;s next?</p>
<p>Among the things we want to tackle for 2.x / 3.0 and beyond, we&rsquo;re interested in <strong>moving the Groovy grammar from Antlr 2.x to a newer version</strong>. Antlr 3 has been available for a while, but our summer attempt at migrating to it haven&rsquo;t been fruitful so far. But we will investigate this move once 2.0 is out, and decide if we go with Antlr 3 or Antlr 4 which should be available when we want to release 3.0.</p>
<p>In the past, we&rsquo;ve mentioned several times a redesigned Meta-Object Protocol (MOP 2). With the lessons learned in the invoke dynamic support of Groovy 2.0 and with the ironing out of the invoke dynamic performance of the JIT, we should be able to have a better vision of how to rearchitect the MOP so that it fully takes advantage of invoke dynamic, for <strong>faster dynamic features</strong>.</p>
<p>There are many other things we&rsquo;d like to have a look at, but it&rsquo;s still a bit too early to tell what&rsquo;s going to be in Groovy 3 and beyond, as of today. We have time to see what&rsquo;s interesting to add to Groovy to <strong>continue helping you being more productive developers</strong>, and we will continue to <strong>welcome and appreciate your feedback</strong> and what you&rsquo;d like to have in Groovy in the future to <strong>simplify your life</strong>.<br />
<strong>Groovy is what it is today thanks to your ideas and contributions!</strong> <strong>Important links</strong> You can view the changelog on JIRA for the details:</p>
<ul>
<li>
<p>Groovy 1.8.4:
<a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=17852">http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=17852</a></p>
</li>
<li>
<p>Groovy 2.0 beta 1:
<a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=17925">http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=17925</a></p>
</li>
</ul>
<p>And you can download the new releases from the usual download area on the Groovy website:<br />
<a href="http://groovy.codehaus.org/Download?nc">http://groovy.codehaus.org/Download?nc</a> Thanks a lot to everybody involved, and we&rsquo;re looking forward to your feedback!</p>
<p>The Groovy development team</p></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Présentation sur PrettyTime et GPars au Paris JUG</title><link>https://glaforge.dev/talks/2011/11/09/presentation-sur-prettytime-et-gpars-au-paris-jug/</link><pubDate>Wed, 09 Nov 2011 00:10:00 +0100</pubDate><guid>https://glaforge.dev/talks/2011/11/09/presentation-sur-prettytime-et-gpars-au-paris-jug/</guid><description>&lt;p>Warning: Sorry, this time, I&amp;rsquo;ll blog in French to share my slides of the presentation in French that I made yesterday at the &lt;a href="http://www.parisjug.org/xwiki/bin/view/Main/WebHome">Paris JUG&lt;/a> about the &lt;a href="http://ocpsoft.com/prettytime/">PrettyTime&lt;/a> library and the &lt;a href="http://gpars.codehaus.org/">GPars&lt;/a> concurrency / parallelism toolkit.&lt;/p>
&lt;p>Hier soir, j&amp;rsquo;ai eu le plaisir de retrouver les passionnés du &lt;a href="http://www.parisjug.org/xwiki/bin/view/Main/WebHome">Paris JUG&lt;/a> pour parler sur le thème des &amp;ldquo;petites librairies&amp;rdquo; utiles que l&amp;rsquo;on peut utiliser sur nos projets. J&amp;rsquo;avais proposé de parler de &lt;a href="http://ocpsoft.org/prettytime/">PrettyTime&lt;/a>, qui permet d&amp;rsquo;écrire des dates relatives du genre &lt;em>&amp;ldquo;il y a 3 minutes&amp;rdquo;&lt;/em> ou &lt;em>&amp;ldquo;dans 4 jours&amp;rdquo;&lt;/em>. Et j&amp;rsquo;avais aussi suggéré de couvrir le sujet de la concurrence et de la parallèlisation avec &lt;a href="http://gpars.codehaus.org/">GPars&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>Warning: Sorry, this time, I&rsquo;ll blog in French to share my slides of the presentation in French that I made yesterday at the <a href="http://www.parisjug.org/xwiki/bin/view/Main/WebHome">Paris JUG</a> about the <a href="http://ocpsoft.com/prettytime/">PrettyTime</a> library and the <a href="http://gpars.codehaus.org/">GPars</a> concurrency / parallelism toolkit.</p>
<p>Hier soir, j&rsquo;ai eu le plaisir de retrouver les passionnés du <a href="http://www.parisjug.org/xwiki/bin/view/Main/WebHome">Paris JUG</a> pour parler sur le thème des &ldquo;petites librairies&rdquo; utiles que l&rsquo;on peut utiliser sur nos projets. J&rsquo;avais proposé de parler de <a href="http://ocpsoft.org/prettytime/">PrettyTime</a>, qui permet d&rsquo;écrire des dates relatives du genre <em>&ldquo;il y a 3 minutes&rdquo;</em> ou <em>&ldquo;dans 4 jours&rdquo;</em>. Et j&rsquo;avais aussi suggéré de couvrir le sujet de la concurrence et de la parallèlisation avec <a href="http://gpars.codehaus.org/">GPars</a>.</p>
<p>J&rsquo;ai publié ma présentation sur slideshare :</p>
<iframe
  style="width: 100%; aspect-ratio: 1.62" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"
  src="http://www.slideshare.net/slideshow/embed_code/10084466?rel=0" allowfullscreen webkitallowfullscreen mozallowfullscreen> </iframe>
<br><br>
<p>Au passage, un grand merci à Paul King et Václav Pech, committers sur GPars, grâce à qui j&rsquo;ai pu réaliser le contenu sur GPars ! (je leur ai même piqué des slides et des exemples)</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A Groovy page on Google+</title><link>https://glaforge.dev/posts/2011/11/09/a-groovy-page-on-google/</link><pubDate>Wed, 09 Nov 2011 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2011/11/09/a-groovy-page-on-google/</guid><description>&lt;p>With Google+ releasing its new &lt;a href="http://googleblog.blogspot.com/2011/11/google-pages-connect-with-all-things.html">&amp;ldquo;page&amp;rdquo; feature&lt;/a>, I&amp;rsquo;ve created a &lt;a href="https://plus.google.com/u/0/101432359761228268146/posts">page for the Groovy project on Google+&lt;/a>. The idea is to post some regular updates on what&amp;rsquo;s going on in the &lt;a href="http://groovy.codehaus.org">Groovy&lt;/a> project, its ecosystem, and share any interesting article that&amp;rsquo;s of interest to those using Groovy.&lt;/p></description><content:encoded>
<![CDATA[<p>With Google+ releasing its new <a href="http://googleblog.blogspot.com/2011/11/google-pages-connect-with-all-things.html">&ldquo;page&rdquo; feature</a>, I&rsquo;ve created a <a href="https://plus.google.com/u/0/101432359761228268146/posts">page for the Groovy project on Google+</a>. The idea is to post some regular updates on what&rsquo;s going on in the <a href="http://groovy.codehaus.org">Groovy</a> project, its ecosystem, and share any interesting article that&rsquo;s of interest to those using Groovy.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Gaelyk presentation at SpringOne2GX</title><link>https://glaforge.dev/talks/2011/11/02/gaelyk-presentation-at-springone2gx/</link><pubDate>Wed, 02 Nov 2011 00:00:00 +0100</pubDate><guid>https://glaforge.dev/talks/2011/11/02/gaelyk-presentation-at-springone2gx/</guid><description>&lt;p>After sharing my slides for the &lt;a href="http://glaforge.appspot.com/article/groovy-domain-specific-languages-in-chicago">DSL&lt;/a> talk, and the &lt;a href="http://glaforge.appspot.com/article/what-s-new-in-groovy-1-8-and-beyond">Groovy update&lt;/a> presentation, I&amp;rsquo;ll finish the tour with my presentation on &lt;a href="http://gaelyk.appspot.com">Gaelyk&lt;/a>.&lt;/p>
&lt;iframe
style="width: 100%; aspect-ratio: 1.62" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"
src="http://www.slideshare.net/slideshow/embed_code/9974808?rel=0" allowfullscreen webkitallowfullscreen mozallowfullscreen> &lt;/iframe>
&lt;br>&lt;br></description><content:encoded>
<![CDATA[<p>After sharing my slides for the <a href="http://glaforge.appspot.com/article/groovy-domain-specific-languages-in-chicago">DSL</a> talk, and the <a href="http://glaforge.appspot.com/article/what-s-new-in-groovy-1-8-and-beyond">Groovy update</a> presentation, I&rsquo;ll finish the tour with my presentation on <a href="http://gaelyk.appspot.com">Gaelyk</a>.</p>
<iframe
  style="width: 100%; aspect-ratio: 1.62" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"
  src="http://www.slideshare.net/slideshow/embed_code/9974808?rel=0" allowfullscreen webkitallowfullscreen mozallowfullscreen> </iframe>
<br><br>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>What's new in Groovy 1.8 and beyond?</title><link>https://glaforge.dev/talks/2011/11/01/what-s-new-in-groovy-1-8-and-beyond/</link><pubDate>Tue, 01 Nov 2011 00:00:00 +0100</pubDate><guid>https://glaforge.dev/talks/2011/11/01/what-s-new-in-groovy-1-8-and-beyond/</guid><description>&lt;p>Last week, at the &lt;a href="http://www.slideshare.net/glaforge">SpringOne2GX conference&lt;/a>, in Chicago, I gave an update on the new features of the recently released Groovy 1.8, as well as new features we&amp;rsquo;re working on for upcoming versions of the language and its APIs.&lt;/p>
&lt;p>I&amp;rsquo;ve uploaded my slides on Slideshare:&lt;/p>
&lt;iframe
style="width: 100%; aspect-ratio: 1.62" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"
src="http://www.slideshare.net/slideshow/embed_code/9973780?rel=0" allowfullscreen webkitallowfullscreen mozallowfullscreen> &lt;/iframe>
&lt;br>&lt;br>
&lt;p>I&amp;rsquo;m covering:&lt;/p>
&lt;ul>
&lt;li>Nicer DSLs with command chains&lt;/li>
&lt;li>Runtime performance improvements&lt;/li>
&lt;li>GPars bundled for taming multicore&lt;/li>
&lt;li>Closure enhancements (functional flavor and closure annotation parameters)&lt;/li>
&lt;li>The builtin JSON support&lt;/li>
&lt;li>The new AST transformations&lt;/li>
&lt;li>The alignment with JDK 7 (Project Coin)&lt;/li>
&lt;li>InvokeDynamic support coming up&lt;/li>
&lt;li>Static type checking&lt;/li>
&lt;li>And other minor improvements&lt;/li>
&lt;/ul></description><content:encoded>
<![CDATA[<p>Last week, at the <a href="http://www.slideshare.net/glaforge">SpringOne2GX conference</a>, in Chicago, I gave an update on the new features of the recently released Groovy 1.8, as well as new features we&rsquo;re working on for upcoming versions of the language and its APIs.</p>
<p>I&rsquo;ve uploaded my slides on Slideshare:</p>
<iframe
  style="width: 100%; aspect-ratio: 1.62" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"
  src="http://www.slideshare.net/slideshow/embed_code/9973780?rel=0" allowfullscreen webkitallowfullscreen mozallowfullscreen> </iframe>
<br><br>
<p>I&rsquo;m covering:</p>
<ul>
<li>Nicer DSLs with command chains</li>
<li>Runtime performance improvements</li>
<li>GPars bundled for taming multicore</li>
<li>Closure enhancements (functional flavor and closure annotation parameters)</li>
<li>The builtin JSON support</li>
<li>The new AST transformations</li>
<li>The alignment with JDK 7 (Project Coin)</li>
<li>InvokeDynamic support coming up</li>
<li>Static type checking</li>
<li>And other minor improvements</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Domain-Specific Languages in Chicago</title><link>https://glaforge.dev/talks/2011/10/31/groovy-domain-specific-languages-in-chicago/</link><pubDate>Mon, 31 Oct 2011 00:00:00 +0100</pubDate><guid>https://glaforge.dev/talks/2011/10/31/groovy-domain-specific-languages-in-chicago/</guid><description>&lt;p>With my friend &lt;a href="https://plus.google.com/105508550588099229829/about">Paul King&lt;/a>, we ran our &lt;a href="http://www.slideshare.net/glaforge/groovy-dsls-from-beginner-to-expert-guillaume-laforge-and-paul-king-springone2gx-2011">Groovy Domain-Specific Languages&lt;/a> talk again this year in Chicago, for the &lt;a href="http://springone2gx.com/conference/chicago/2011/10/2gx/event_sessions">SpringOne2GX conference&lt;/a>. I&amp;rsquo;ve uploaded the slides on Slideshare, and Paul has pushed the &lt;a href="https://github.com/paulk-asert/DSLsFromBeginnerToExpert">examples on Github&lt;/a>. The room was packed, and we had lots of fun doing our little dialogue between the customer and Groovy DSL developer, improving our DSLs, showing new techniques along the way, to suit the requirements as they evolved.&lt;/p>
&lt;iframe
style="width: 100%; aspect-ratio: 1.62" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"
src="http://www.slideshare.net/slideshow/embed_code/9957057?rel=0" allowfullscreen webkitallowfullscreen mozallowfullscreen> &lt;/iframe>
&lt;br>&lt;br></description><content:encoded>
<![CDATA[<p>With my friend <a href="https://plus.google.com/105508550588099229829/about">Paul King</a>, we ran our <a href="http://www.slideshare.net/glaforge/groovy-dsls-from-beginner-to-expert-guillaume-laforge-and-paul-king-springone2gx-2011">Groovy Domain-Specific Languages</a> talk again this year in Chicago, for the <a href="http://springone2gx.com/conference/chicago/2011/10/2gx/event_sessions">SpringOne2GX conference</a>. I&rsquo;ve uploaded the slides on Slideshare, and Paul has pushed the <a href="https://github.com/paulk-asert/DSLsFromBeginnerToExpert">examples on Github</a>. The room was packed, and we had lots of fun doing our little dialogue between the customer and Groovy DSL developer, improving our DSLs, showing new techniques along the way, to suit the requirements as they evolved.</p>
<iframe
  style="width: 100%; aspect-ratio: 1.62" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"
  src="http://www.slideshare.net/slideshow/embed_code/9957057?rel=0" allowfullscreen webkitallowfullscreen mozallowfullscreen> </iframe>
<br><br>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>SpringOne2GX, Chicago, this week</title><link>https://glaforge.dev/posts/2011/10/24/springone2gx-chicago-this-week/</link><pubDate>Mon, 24 Oct 2011 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/10/24/springone2gx-chicago-this-week/</guid><description>&lt;p>It&amp;rsquo;s the conference season. After JavaOne, comes &lt;a href="http://springone2gx.com/conference/chicago/2011/10/home">SpringOne2GX&lt;/a>, in Chicago, this week (October 25th till 28th). Then &lt;a href="http://greach.es/">Greach&lt;/a> in Madrid (Spain), &lt;a href="http://devoxx.com/display/DV11/Home">Devoxx&lt;/a> in Antwerp (Belgium) and we&amp;rsquo;ll finish with &lt;a href="http://skillsmatter.com/event/java-jee/groovy-grails-exchange-2011">Groovy/Grails eXchange&lt;/a> in London (UK).&lt;/p>
&lt;p>In Chicago this week, we&amp;rsquo;ll have 4 tracks dedicated to Groovy, Grails, and the big Groovy ecosystem (Gradle, Griffon, Spock, Geb&amp;hellip;) over three days. That&amp;rsquo;s a lot of sessions and speakers ready to tell you all about what&amp;rsquo;s rocking in our world!&lt;/p></description><content:encoded>
<![CDATA[<p>It&rsquo;s the conference season. After JavaOne, comes <a href="http://springone2gx.com/conference/chicago/2011/10/home">SpringOne2GX</a>, in Chicago, this week (October 25th till 28th). Then <a href="http://greach.es/">Greach</a> in Madrid (Spain), <a href="http://devoxx.com/display/DV11/Home">Devoxx</a> in Antwerp (Belgium) and we&rsquo;ll finish with <a href="http://skillsmatter.com/event/java-jee/groovy-grails-exchange-2011">Groovy/Grails eXchange</a> in London (UK).</p>
<p>In Chicago this week, we&rsquo;ll have 4 tracks dedicated to Groovy, Grails, and the big Groovy ecosystem (Gradle, Griffon, Spock, Geb&hellip;) over three days. That&rsquo;s a lot of sessions and speakers ready to tell you all about what&rsquo;s rocking in our world!</p>
<p>Be sure to check the <a href="http://springone2gx.com/conference/chicago/2011/10/2gx/event_sessions">agenda</a> and the nice line-up of <a href="http://springone2gx.com/conference/chicago/2011/10/speakers">speakers</a>.</p>
<p>I&rsquo;ll be presenting three talks:</p>
<ul>
<li>Groovy Update: What&rsquo;s new in Groovy 1.8 and what&rsquo;s coming</li>
<li>Groovy DSLs, from beginner to expert, with my friend Paul King, that&rsquo;ll be a new edition of our past year&rsquo;s talk</li>
<li>Gaelyk, Groovy coding to the cloud</li>
</ul>
<p>I&rsquo;m looking forward to meeting you all in Chicago!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 1.8.3 and 1.9-beta-4 released</title><link>https://glaforge.dev/posts/2011/10/12/groovy-1-8-3-and-1-9-beta-4-released/</link><pubDate>Wed, 12 Oct 2011 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/10/12/groovy-1-8-3-and-1-9-beta-4-released/</guid><description>&lt;p>The &lt;a href="http://groovy.codehaus.org">Groovy&lt;/a> development team has just released Groovy 1.8.3 and 1.9-beta-4.&lt;/p>
&lt;p>For the impatients:&lt;/p>
&lt;ul>
&lt;li>download it on the &lt;a href="http://groovy.codehaus.org/Download">Groovy download page&lt;/a>&lt;/li>
&lt;li>check the &lt;a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;version=17657">JIRA release notes for 1.8.3&lt;/a> and &lt;a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;version=17656">1.9-beta-4&lt;/a>&lt;/li>
&lt;li>or read the &lt;a href="http://markmail.org/message/ugk5244bdqkzxfil">official announcement&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Those two releases are essentially about bug fixes and minor enhancements, but we also have interesting developments going on for static type checking your Groovy code, and for invoke dynamic support for performance of our dynamic core. But it&amp;rsquo;ll be better that I save those topics for some future posts!&lt;/p></description><content:encoded>
<![CDATA[<p>The <a href="http://groovy.codehaus.org">Groovy</a> development team has just released Groovy 1.8.3 and 1.9-beta-4.</p>
<p>For the impatients:</p>
<ul>
<li>download it on the <a href="http://groovy.codehaus.org/Download">Groovy download page</a></li>
<li>check the <a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=17657">JIRA release notes for 1.8.3</a> and <a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=17656">1.9-beta-4</a></li>
<li>or read the <a href="http://markmail.org/message/ugk5244bdqkzxfil">official announcement</a></li>
</ul>
<p>Those two releases are essentially about bug fixes and minor enhancements, but we also have interesting developments going on for static type checking your Groovy code, and for invoke dynamic support for performance of our dynamic core. But it&rsquo;ll be better that I save those topics for some future posts!</p>
<p>Here, I just wanted to highlight some of the minor enhancements in those two new releases. Such enhancements sometimes go unnoticed, so I thought it would be nice to put them a bit more in the spotligh.</p>
<p>Customize your groovysh prompt A pretty <a href="http://jira.codehaus.org/browse/GROOVY-2832">old request</a> was about the ability to customize your groovysh prompt, so that instead of <code>groovy</code>, you could have your own message. This can be handy for people reusing the groovy shell infrastructure to embed that somehow in their applications, offering various pre-defined variables in the binding, etc.</p>
<p>If you want to customize your groovysh prompt, there are two approaches: either you export an environement variable called <code>GROOVYSH_PROMPT</code>, or you pass the <code>-Dgroovysh.prompt=myMessage</code> argument to your groovysh launch command.</p>
<p>Here&rsquo;s a screenshot showing how one can use fancy characters from a Mac OS X console:</p>
<p><figure>
  <a href="#img-10d1a00aabdb32827a53095236d0a7bf">
    <img src="/img/misc/groovysh&#43;prompt.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-10d1a00aabdb32827a53095236d0a7bf">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/groovysh&#43;prompt.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Launching remote Groovy scripts The groovy command usually takes a file as argument, pointing at the script or class you want to run. But it&rsquo;s now also possible to run remote scripts available on the net somewhere.</p>
<p>For example, with the <a href="http://groovyconsole.appspot.com">Groovy Web Console</a>, scripts can be viewed with the following URL: <a href="http://groovyconsole.appspot.com/script/110001">http://groovyconsole.appspot.com/script/110001</a>. But there&rsquo;s also another URL that you can use to get the raw content of that script: <a href="http://groovyconsole.appspot.com/raw/110001">http://groovyconsole.appspot.com/raw/110001</a>.</p>
<p>So just type the following, to run a remote Hello World script!</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>groovy http://groovyconsole.appspot.com/raw/110001
</span></span></code></pre></div><p>And it&rsquo;ll just print hello on the console.</p>
<p>As a little warning though, don&rsquo;t run blindly any remote script that someone sends you the link of :-)</p>
<p>Formatting dates for a specific time zone Groovy&rsquo;s had some nice shortcuts for quickly formatting dates and calendar instances. And there&rsquo;s a new little variant for printing a particular date and time according to a certain time zone.</p>
<p>I&rsquo;m currently in GMT+2, and I want to print the current date and time with my particular time zone, here&rsquo;s how I could do it:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> d <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Date<span style="color:#666">()</span>  
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> tz <span style="color:#666">=</span> TimeZone<span style="color:#666">.</span><span style="color:#4070a0">getTimeZone</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;GMT+2&#39;</span><span style="color:#666">)</span>  
</span></span><span style="display:flex;"><span>println d<span style="color:#666">.</span><span style="color:#4070a0">format</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;yyyy-MM-dd HH:mmZ&#39;</span><span style="color:#666">,</span> tz<span style="color:#666">)</span>  
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// prints for me now: &#34;2011-10-13 00:53+0200&#34;
</span></span></span></code></pre></div><p>Now onto two new features which are exclusively on Groovy 1.9.</p>
<p>Viewing the generated bytecode from the Groovy console This capability is probably more interesting for the Groovy developers themselves, as most of your time, you won&rsquo;t really need to know what the groovy compiler generates in terms of bytecode — not even mentioning that this might be a little bit scary. But if you&rsquo;re using the Groovy Swing console, that you&rsquo;re launching the AST browser, it&rsquo;ll show you the bytecode as well, as shows the following screenshot.</p>
<p><figure>
  <a href="#img-b34e1ca8757be916f43beb12655b8610">
    <img src="/img/misc/bytecode&#43;view.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-b34e1ca8757be916f43beb12655b8610">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/bytecode&#43;view.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>So first step, launch your Groovy console with the groovyconsole command. Then open a script or class whose bytecode you want to have a look at. Click on the AST browser menu item, or hit <code>CTRL-T</code>. At that point, make sure you use the &ldquo;class generation&rdquo; phase from the drop-down box, and also select a <em>class node</em> in the AST tree (otherwise you won&rsquo;t see any bytecode at all).</p>
<p>Control over Global AST Transformations Global AST transformations like Grape&rsquo;s or the AST builder are not necessarily needed when you embed Groovy in your application. They incur a pretty small overhead of compilation time. Or perhaps on your classpath you use a library that has a global transform but that you are not using and don&rsquo;t want to be impacted by it. Now you are able to disable such transformations through the CompilerConfiguration object.</p>
<p>Here&rsquo;s an excerpt of our unit test covering this new capability:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.codehaus.groovy.control.CompilerConfiguration</span>  
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> config <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> CompilerConfiguration<span style="color:#666">()</span>  
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>config<span style="color:#666">.</span><span style="color:#4070a0">disabledGlobalASTTransformations</span> <span style="color:#666">=</span>   
</span></span><span style="display:flex;"><span>    <span style="color:#666">[</span><span style="color:#4070a0">&#39;org.codehaus.groovy.ast.builder.AstBuilderTransformation&#39;</span><span style="color:#666">]</span>  
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> script <span style="color:#666">=</span> <span style="color:#4070a0">&#39;&#39;&#39;  
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    import org.codehaus.groovy.ast.builder.AstBuilder  
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    new AstBuilder().buildFromCode { &#34;Hello&#34; }  
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">&#39;&#39;&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// witout a specific configuration, the AST builder works  
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> shell <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> GroovyShell<span style="color:#666">()</span>  
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> shell<span style="color:#666">.</span><span style="color:#4070a0">evaluate</span><span style="color:#666">(</span>script<span style="color:#666">).</span><span style="color:#4070a0">class</span> <span style="color:#666">==</span> ArrayList  
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// now with the configuration in place,   
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// the AST builder transform is not applied  
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>shell <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> GroovyShell<span style="color:#666">(</span>config<span style="color:#666">)</span>  
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>shouldFail <span style="color:#666">{</span>  
</span></span><span style="display:flex;"><span>    shell<span style="color:#666">.</span><span style="color:#4070a0">evaluate</span><span style="color:#666">(</span>script<span style="color:#666">)</span>  
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Google announces Dart, new language for the web</title><link>https://glaforge.dev/posts/2011/10/10/google-announces-dart-new-language-for-the-web/</link><pubDate>Mon, 10 Oct 2011 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/10/10/google-announces-dart-new-language-for-the-web/</guid><description>&lt;p>Google &lt;a href="http://blog.chromium.org/2011/10/dart-language-for-structured.html">announces Dart&lt;/a>, at the &lt;a href="http://gotocon.com/aarhus-2011/presentation/Opening%20Keynote:%20Dart,%20a%20new%20programming%20language%20for%20structured%20web%20programming">Goto Conference&lt;/a>: a new programming language for &amp;ldquo;structured web programming&amp;rdquo;, for building web applications. Google also launched the &lt;a href="http://www.dartlang.org/">Dart website&lt;/a>, where you can read about the rationale of the language, the &lt;a href="http://www.dartlang.org/docs/spec/dartLangSpec.pdf">specification&lt;/a>, and play with some live examples.&lt;/p>
&lt;h2 id="the-team-behind-dart">The team behind Dart&lt;/h2>
&lt;p>Reknown engineers &lt;a href="http://gotocon.com/aarhus-2011/speaker/Lars+Bak">Lars Bak&lt;/a> and &lt;a href="http://gotocon.com/aarhus-2011/speaker/Gilad+Bracha">Gilad Bracha&lt;/a> are on stage to present the language. Lars Bak worked on many virtual machines like Sun&amp;rsquo;s HotSpot or more recently &lt;a href="http://code.google.com/p/v8/">Google V8&lt;/a>, whereas Gilad Bracha is also famous for his work on the Java specification, his Smalltalk background, and the creation of a recent language called &lt;a href="http://newspeaklanguage.org/">Newspeak&lt;/a> (a flavour of Smalltalk). So with such a team of VM and language experts, you can expect some interesting outcome.&lt;/p></description><content:encoded>
<![CDATA[<p>Google <a href="http://blog.chromium.org/2011/10/dart-language-for-structured.html">announces Dart</a>, at the <a href="http://gotocon.com/aarhus-2011/presentation/Opening%20Keynote:%20Dart,%20a%20new%20programming%20language%20for%20structured%20web%20programming">Goto Conference</a>: a new programming language for &ldquo;structured web programming&rdquo;, for building web applications. Google also launched the <a href="http://www.dartlang.org/">Dart website</a>, where you can read about the rationale of the language, the <a href="http://www.dartlang.org/docs/spec/dartLangSpec.pdf">specification</a>, and play with some live examples.</p>
<h2 id="the-team-behind-dart">The team behind Dart</h2>
<p>Reknown engineers <a href="http://gotocon.com/aarhus-2011/speaker/Lars+Bak">Lars Bak</a> and <a href="http://gotocon.com/aarhus-2011/speaker/Gilad+Bracha">Gilad Bracha</a> are on stage to present the language. Lars Bak worked on many virtual machines like Sun&rsquo;s HotSpot or more recently <a href="http://code.google.com/p/v8/">Google V8</a>, whereas Gilad Bracha is also famous for his work on the Java specification, his Smalltalk background, and the creation of a recent language called <a href="http://newspeaklanguage.org/">Newspeak</a> (a flavour of Smalltalk). So with such a team of VM and language experts, you can expect some interesting outcome.</p>
<p>But beyond those two well known figures, it seems Google has aligned a <a href="http://code.google.com/p/dart/people/list?num=100&amp;start=0">big list of engineers</a> to work on the project, as you can see on the Google code page. What is interesting as well, is that Bob Nystrom is also part of the team: Bob created the <a href="http://magpie.stuffwithstuff.com/">Magpie language</a>, which is a really creative, elegant, and innovative language. I hope Dart can get pattern matching as nicely as Magpie has it.<br />
What&rsquo;s Dart?</p>
<p>Dart is a simple Object-Oriented language with single inheritance, class-based, with optional static types, and is single threaded. Dart resembles a mix of Java and JavaScript, when you look at the samples on the <a href="http://www.dartlang.org/">Dart website</a>.<br />
Dart&rsquo;s objectives are:</p>
<blockquote>
<ul>
<li>Create a <strong>structured yet flexible</strong> language for web programming.*   Make Dart feel <strong>familiar and natural</strong> to programmers and thus easy to learn.*   Ensure that Dart delivers <strong>high performance</strong> on all modern web browsers and environments ranging from small handheld devices to server-side execution.</li>
</ul></blockquote>
<h2 id="optional-static-types">Optional static types</h2>
<p>The choice of optional static typing is interesting, as it&rsquo;s also a choice we made with <a href="http://groovy.codehaus.org">Groovy</a>. As Gilad coined it: &ldquo;you are innocent until proven guilty on types&rdquo;. Types are there when you need them, especially for larger-scale applications, where types simplify refactoring types, for example. But types don&rsquo;t get in the way when you don&rsquo;t need them.</p>
<p>Using types will help developers express their intent more clearly, will help IDEs and other tools to better understand the structure of your applications, and less documentation will be needed as types will serve as a cue of what your objects at hand are all about. But in places where you don&rsquo;t need types (think inside your method bodies, and quick scripts or prototypes, etc), you can just omit the type information, and you&rsquo;ll only get warnings from the compiler if you make mistakes with the types.</p>
<h2 id="dedicated-vm-or-compiled-to-javascript">Dedicated VM or compiled to JavaScript</h2>
<p>Dart code will be able to run either on a dedicated VM, or compiled to JavaScript so as to run on any modern browser (Chrome, Safari 5+, Firefox 4+). At some point, the VM might be integrated in Google Chrome. And you&rsquo;ll also be able to run Dart on the server-side, like this is the case with JavaScript and Google V8, for instance.</p>
<h2 id="isolates-an-actor-like-concurrency-model">Isolates, an actor-like concurrency model</h2>
<p>Although Dart is single-threaded, its answer to concurrency comes without locking or shared memory, in the form of an actor-like model of concurrency, like in <a href="http://gpars.codehaus.org/Actor?nc">Groovy&rsquo;s GPars</a>. Those actors are actually called &ldquo;isolates&rdquo;.</p>
<h2 id="miscelanous">Miscelanous</h2>
<p>Here are some additional notes I took while looking at the documentation on the Dart website:</p>
<ul>
<li>Dart supports interpolated strings like <a href="http://groovy.codehaus.org/Strings+and+GString">Groovy&rsquo;s GString</a>, supporting both 
  <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi>n</mi><mi>a</mi><mi>m</mi><mi>e</mi></mrow><mi>o</mi><mi>r</mi></mrow><annotation encoding="application/x-tex">{name} or </annotation></semantics></math></span>

name like Groovy.</li>
<li>The visibility is quite simple, with just public and private. Private fields or classes are actually following a naming convention: prefixed with an underscore.</li>
<li>Dart requires semicolons, what a pity!</li>
<li>Dart has functions / closures, with a syntax like <code>(var a, var b) { body }</code>. Think JavaScript&rsquo;s function, but without the function keyword.</li>
<li>Dart supports getters and setters with a C#-like approach.</li>
<li>A fat arrow =&gt; is used for returning a value (when you don&rsquo;t need a full curly-braced function body but just want to return a value, for example for getters).</li>
<li>There&rsquo;s just one constructor for your classes, but you can somehow add additional named constructors with a method named prefixed by the class name.</li>
<li>The <a href="https://code.google.com/p/dart/source/browse/branches/bleeding_edge/dart/language/grammar/Dart.g">grammar</a> was developed with Antlr.</li>
<li>There are a <a href="http://www.dartlang.org/samples/index.html">bunch of samples</a> to look at that are available on Google Code.</li>
</ul>
<h2 id="status-and-more">Status and more</h2>
<p>Dart is not quite done yet, and it seems certain aspects still need to be worked on, like reflection support, rest arguments, enums, pattern matching, etc. But it seems those features are also waiting for user support, as the Dart team is looking for feedback from the community. But despite its unfinished state, it seems Dart is already quite fast, as Dart is already faster than V8 JavaScript when it was initially launched.</p>
<p>I&rsquo;m sure the <a href="https://twitter.com/#!/dart_lang">Twitter account for Dart</a> will quickly get a lot of followers. It&rsquo;s always interesting to follow the birth and evolution of new languages!</p>
<h2 id="update">Update:</h2>
<ul>
<li>A <a href="http://jlouisramblings.blogspot.com/2011/10/musings-on-dart.html">nice analysis on the language features</a></li>
<li>The draft <a href="http://www.dartlang.org/docs/spec/dartLangSpec.pdf">Dart language specification</a> is also interesting to read (although the document needs some concrete examples in various places to make it more readable)</li>
<li>The <a href="http://gotocon.com/dl/goto-aarhus-2011/slides/GiladBracha_and_LarsBak_OpeningKeynoteDartANewProgrammingLanguageForStructuredWebProgramming.pdf">slides of the presentation</a> at GotoCon</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>GR8Conf is coming to Australia</title><link>https://glaforge.dev/posts/2011/10/04/gr8conf-is-coming-to-australia/</link><pubDate>Tue, 04 Oct 2011 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/10/04/gr8conf-is-coming-to-australia/</guid><description>&lt;p>The now famous &lt;a href="http://gr8conf.org">GR8Conf&lt;/a> conference dedicated to all things &lt;a href="http://groovy.codehaus.org">Groovy&lt;/a>, is now &lt;a href="http://www.gr8conf.org/blog/2011/10/04/111">coming to Australia&lt;/a>, as announced on the &lt;a href="http://www.gr8conf.org/blog/list">GR8Conf blog&lt;/a>! It will take place in Canberra, side by side with the &lt;a href="http://osdc.com.au/">Open Source Developers Conference&lt;/a> (OSDC), on November 15th.&lt;/p>
&lt;p>The &lt;a href="http://gr8confau.org/">GR8Conf Australia website&lt;/a> is already up and is running a Call for Papers. So if you have a presentation on Groovy, Grails, Griffon, Gradle, Spock, CodeNarc, Gaelyk, etc, with introductions, deep dives, case studies, and so on, feel free to submit a talk!&lt;/p></description><content:encoded>
<![CDATA[<p>The now famous <a href="http://gr8conf.org">GR8Conf</a> conference dedicated to all things <a href="http://groovy.codehaus.org">Groovy</a>, is now <a href="http://www.gr8conf.org/blog/2011/10/04/111">coming to Australia</a>, as announced on the <a href="http://www.gr8conf.org/blog/list">GR8Conf blog</a>! It will take place in Canberra, side by side with the <a href="http://osdc.com.au/">Open Source Developers Conference</a> (OSDC), on November 15th.</p>
<p>The <a href="http://gr8confau.org/">GR8Conf Australia website</a> is already up and is running a Call for Papers. So if you have a presentation on Groovy, Grails, Griffon, Gradle, Spock, CodeNarc, Gaelyk, etc, with introductions, deep dives, case studies, and so on, feel free to submit a talk!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy AST transformations tutorials</title><link>https://glaforge.dev/posts/2011/10/02/groovy-ast-transformations-tutorials/</link><pubDate>Sun, 02 Oct 2011 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/10/02/groovy-ast-transformations-tutorials/</guid><description>&lt;p>&lt;a href="http://groovy.codehaus.org">Groovy&lt;/a> is a powerful language that gives the opportunity to its users to plugin into the compilation process to create what we call AST transformations, ie. the ability to customize the Abstract Syntax Tree representing your programs before the compiler walks this tree to generate Java bytecode.&lt;/p>
&lt;p>Since Groovy 1.6, many useful such transformations have been added to Groovy, like &lt;a href="http://groovy.codehaus.org/Delegate+transformation">@Delegate&lt;/a> to implement delegation, &lt;a href="http://groovy.codehaus.org/Immutable+transformation">@Immutable&lt;/a> to make your types immutable, or &lt;a href="http://groovy.codehaus.org/Grape">@Grab&lt;/a> to add dependencies to your scripts, and many more. However, mastering the Groovy AST and compiler APIs is not such a simple task, and requires some advanced knowledge of the inner workings of Groovy.&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://groovy.codehaus.org">Groovy</a> is a powerful language that gives the opportunity to its users to plugin into the compilation process to create what we call AST transformations, ie. the ability to customize the Abstract Syntax Tree representing your programs before the compiler walks this tree to generate Java bytecode.</p>
<p>Since Groovy 1.6, many useful such transformations have been added to Groovy, like <a href="http://groovy.codehaus.org/Delegate+transformation">@Delegate</a> to implement delegation, <a href="http://groovy.codehaus.org/Immutable+transformation">@Immutable</a> to make your types immutable, or <a href="http://groovy.codehaus.org/Grape">@Grab</a> to add dependencies to your scripts, and many more. However, mastering the Groovy AST and compiler APIs is not such a simple task, and requires some advanced knowledge of the inner workings of Groovy.</p>
<p><a href="http://joesgroovyblog.blogspot.com/">Joachim Baumann</a>, Groovy committer and <a href="http://www.dpunkt.de/buecher/2610.html">German Groovy book</a> author, wrote a very nice series of articles on the topic of authoring AST transformations, covering:</p>
<ul>
<li><a href="http://joesgroovyblog.blogspot.com/2011/09/ast-transformations-compiler-phases-and.html">compiler phases and syntax trees</a></li>
<li><a href="http://joesgroovyblog.blogspot.com/2011/09/ast-transformations-prerequisites-and.html">prerequisites and annotations</a></li>
<li><a href="http://joesgroovyblog.blogspot.com/2011/09/ast-transformations-transformation.html">the implementation of the example transformation itself</a></li>
<li><a href="http://joesgroovyblog.blogspot.com/2011/09/ast-transformations-testing-and-error.html">testing and error messages</a></li>
<li><a href="http://joesgroovyblog.blogspot.com/2011/10/ast-transformation-using-astbuilder.html">creating a complex AST transformation</a></li>
</ul>
<p>If you&rsquo;re interested in writing your own transformations, those very well written articles are really a great start that should get you up to speed very rapidly.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Lots of Gradle news and activity</title><link>https://glaforge.dev/posts/2011/09/29/lots-of-gradle-news-and-activity/</link><pubDate>Thu, 29 Sep 2011 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/09/29/lots-of-gradle-news-and-activity/</guid><description>&lt;p>The &lt;a href="http://gradle.org">Gradle&lt;/a> project is moving quickly towards its final version and we hear more and more projects adopting or migrating to Gradle for their build and project automation.
Gradle founder Hans Dokter pushed very interesting posts those past days:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://forums.gradle.org/gradle/topics/on_our_way_to_gradle_1_0">On our way to Gradle 1.0&lt;/a>: where he explains the current features being worked on before being able to reach 1.0, with a strong focus on three key aspects: the new dependency cache, the daemon, and performance improvements.&lt;/li>
&lt;li>In &lt;a href="http://forums.gradle.org/gradle/topics/welcome_to_our_new_dependency_cache">Welcome to the new Gradle dependency cache&lt;/a>, Hans details the current critical issues that all other build systems face with the non-reproducability of their builds due to the poor design of the current dependency cache solutions, and then goes on to explain how Gradle&amp;rsquo;s new solution solves all these problems in an elegant way.&lt;/li>
&lt;/ul>
&lt;p>Last but not least, you should also check out the &amp;ldquo;&lt;a href="http://forums.gradle.org/gradle/tags/this_week_in_gradle">week in Gradle&lt;/a>&amp;rdquo; column, which will give you the latest news on everything Gradle every week, starting &lt;a href="http://forums.gradle.org/gradle/topics/this_week_in_gradle_2011_39">this week&lt;/a>, covering the interesting project development, job offerings, appearance at conferences, tweets, articles, and more.&lt;/p></description><content:encoded>
<![CDATA[<p>The <a href="http://gradle.org">Gradle</a> project is moving quickly towards its final version and we hear more and more projects adopting or migrating to Gradle for their build and project automation.
Gradle founder Hans Dokter pushed very interesting posts those past days:</p>
<ul>
<li><a href="http://forums.gradle.org/gradle/topics/on_our_way_to_gradle_1_0">On our way to Gradle 1.0</a>: where he explains the current features being worked on before being able to reach 1.0, with a strong focus on three key aspects: the new dependency cache, the daemon, and performance improvements.</li>
<li>In <a href="http://forums.gradle.org/gradle/topics/welcome_to_our_new_dependency_cache">Welcome to the new Gradle dependency cache</a>, Hans details the current critical issues that all other build systems face with the non-reproducability of their builds due to the poor design of the current dependency cache solutions, and then goes on to explain how Gradle&rsquo;s new solution solves all these problems in an elegant way.</li>
</ul>
<p>Last but not least, you should also check out the &ldquo;<a href="http://forums.gradle.org/gradle/tags/this_week_in_gradle">week in Gradle</a>&rdquo; column, which will give you the latest news on everything Gradle every week, starting <a href="http://forums.gradle.org/gradle/topics/this_week_in_gradle_2011_39">this week</a>, covering the interesting project development, job offerings, appearance at conferences, tweets, articles, and more.</p>
<p><strong>Update #1:</strong> Gradle now has a new logo! So I updated the picture in my post as well.</p>
<p><strong>Update #2:</strong> There is now a special <a href="http://gradle.org/roadmap">Roadmap page</a> to see what&rsquo;s planned for upcoming versions and releases.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy on GitHub</title><link>https://glaforge.dev/posts/2011/09/19/groovy-on-github/</link><pubDate>Mon, 19 Sep 2011 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/09/19/groovy-on-github/</guid><description>&lt;p>Groovy completed successfully its move to the Git SCM this summer, thanks to &lt;a href="http://ambientideas.com/">Matthew&lt;/a>&amp;rsquo;s help. For helping everybody get up-to-speed with Git, I&amp;rsquo;ve added a &lt;a href="http://groovy.codehaus.org/Git">page about Git on the Groovy wiki&lt;/a>.&lt;/p>
&lt;p>We also now have a &lt;strong>&amp;ldquo;groovy&amp;rdquo; organization on GitHub&lt;/strong>: &lt;a href="https://github.com/groovy/groovy-core">https://github.com/groovy/groovy-core&lt;/a>&lt;/p>
&lt;p>Our three main branches have been pushed there: master, &lt;code>GROOVY_1_8_X&lt;/code> and &lt;code>GROOVY_1_7_X&lt;/code>. But I&amp;rsquo;ve not pushed the tags though.&lt;/p>
&lt;p>The &lt;strong>GitHub repository serves as a mirror&lt;/strong>, and the Groovy despots will be able to regularly push the latest changes to GitHub, and deal with pull requests and such.&lt;/p></description><content:encoded>
<![CDATA[<p>Groovy completed successfully its move to the Git SCM this summer, thanks to <a href="http://ambientideas.com/">Matthew</a>&rsquo;s help. For helping everybody get up-to-speed with Git, I&rsquo;ve added a <a href="http://groovy.codehaus.org/Git">page about Git on the Groovy wiki</a>.</p>
<p>We also now have a <strong>&ldquo;groovy&rdquo; organization on GitHub</strong>: <a href="https://github.com/groovy/groovy-core">https://github.com/groovy/groovy-core</a></p>
<p>Our three main branches have been pushed there: master, <code>GROOVY_1_8_X</code> and <code>GROOVY_1_7_X</code>. But I&rsquo;ve not pushed the tags though.</p>
<p>The <strong>GitHub repository serves as a mirror</strong>, and the Groovy despots will be able to regularly push the latest changes to GitHub, and deal with pull requests and such.</p>
<p><strong>Update #1:</strong> The branches as well as all the tags have been pushed to the GitHub mirror as well.</p>
<p><strong>Update #2:</strong> We&rsquo;ve even had our first pull requests! Hooray :-)</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Playing with the Google+ APIs in Groovy</title><link>https://glaforge.dev/posts/2011/09/15/playing-with-the-google-apis/</link><pubDate>Thu, 15 Sep 2011 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/09/15/playing-with-the-google-apis/</guid><description>&lt;p>As soon as I heard about the &lt;a href="http://googleplusplatform.blogspot.com/2011/09/getting-started-on-google-api.html">opening of the Google+ APIs&lt;/a>, letting us access public posts in our Google+ streams, I wanted to play with them, and integrate my posts in my social stream on my blog.&lt;/p>
&lt;p>First of all, a little screenshot, where you&amp;rsquo;ll see the Google+ item at the bottom:&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-e95dd1f03a90b18fee892313345b26a6">
&lt;img src="https://glaforge.dev/img/misc/gplus-social.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-e95dd1f03a90b18fee892313345b26a6">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/gplus-social.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>Now, a bit of code? Yeah, of course. First of all, you&amp;rsquo;ll need to follow the explanations of the Google+ API portal to get your own API key (I&amp;rsquo;ll hide mine in this example so you don&amp;rsquo;t use mine). Then, it&amp;rsquo;s really just a handful lines of Groovy code to get the integration goin, thanks to &lt;a href="http://docs.codehaus.org/display/GROOVY/Groovy+1.8+release+notes#Groovy1.8releasenotes-NativeJSONsupport">Groovy&amp;rsquo;s Json support&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>As soon as I heard about the <a href="http://googleplusplatform.blogspot.com/2011/09/getting-started-on-google-api.html">opening of the Google+ APIs</a>, letting us access public posts in our Google+ streams, I wanted to play with them, and integrate my posts in my social stream on my blog.</p>
<p>First of all, a little screenshot, where you&rsquo;ll see the Google+ item at the bottom:</p>
<p><figure>
  <a href="#img-e95dd1f03a90b18fee892313345b26a6">
    <img src="/img/misc/gplus-social.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-e95dd1f03a90b18fee892313345b26a6">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/gplus-social.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Now, a bit of code? Yeah, of course. First of all, you&rsquo;ll need to follow the explanations of the Google+ API portal to get your own API key (I&rsquo;ll hide mine in this example so you don&rsquo;t use mine). Then, it&rsquo;s really just a handful lines of Groovy code to get the integration goin, thanks to <a href="http://docs.codehaus.org/display/GROOVY/Groovy+1.8+release+notes#Groovy1.8releasenotes-NativeJSONsupport">Groovy&rsquo;s Json support</a>.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.json.</span><span style="">\</span><span style="color:#666">*</span>  
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">java.text.SimpleDateFormat</span>  
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// date format: 2011-09-09T08:34:07.000Z  
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> sdf <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> SimpleDateFormat<span style="color:#666">(</span><span style="color:#4070a0">&#34;yyyy-MM-dd&#39;T&#39;HH:mm:ss.SSS&#39;Z&#39;&#34;</span><span style="color:#666">)</span>  
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// my top secret API key  
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> myKey <span style="color:#666">=</span> <span style="color:#4070a0">&#34;...&#34;</span>  
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// that&#39;s the ID you&#39;ll find in your Google+ profile URL  
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> myProfileId <span style="color:#666">=</span> <span style="color:#4070a0">&#34;114130972232398734985&#34;</span>  
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// and this is the URL of my activities  
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> myActivities <span style="color:#666">=</span> <span style="color:#4070a0">&#34;https://www.googleapis.com/plus/v1/people/${myProfileId}/activities/public?key=${myKey}&#34;</span><span style="color:#666">.</span><span style="color:#4070a0">toURL</span><span style="color:#666">().</span><span style="color:#4070a0">text</span>  
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// I simply parse the JSON payload  
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> root <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> JsonSlurper<span style="color:#666">().</span><span style="color:#4070a0">parseText</span><span style="color:#666">(</span>myActivities<span style="color:#666">)</span>  
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// I iterate over all the items, and print the date, the URL of the post, and the title  
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>root<span style="color:#666">.</span><span style="color:#4070a0">items</span><span style="color:#666">.</span><span style="color:#4070a0">each</span> <span style="color:#666">{</span>   
</span></span><span style="display:flex;"><span>    println <span style="color:#4070a0">&#34;${sdf.parse(it.published)} - ${it.url} - ${it.title.replaceAll(/\\s+/, &#39; &#39;)}&#34;</span>   
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy, fastest growing language according to eWeek</title><link>https://glaforge.dev/posts/2011/09/13/groovy-fastest-growing-language-according-to-eweek/</link><pubDate>Tue, 13 Sep 2011 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/09/13/groovy-fastest-growing-language-according-to-eweek/</guid><description>&lt;p>I&amp;rsquo;ve just come across &lt;a href="http://www.eweek.com/c/a/Application-Development/Groovy-JavaScript-Ruby-Among-Fastest-Growing-Programming-Languages-505803/">eWeek&amp;rsquo;s latest article on programming languages&lt;/a> trends, and they announce that Groovy is the fastest growing language!&lt;/p>
&lt;blockquote>
&lt;p>The data shows the percentage of jobs that have increased (or decreased) for various programming languages since November 2009. In the time frame this slide show depicts, Groovy saw the largest increase in jobs, followed by C# and—surprisingly—COBOL.&lt;/p>&lt;/blockquote>
&lt;p>Of course, we still have a lot of work to do to continue growing, and stay the most popular and successful alternative language for the JVM, but it&amp;rsquo;s great to see such confirmations on a famous online business journal.&lt;/p></description><content:encoded>
<![CDATA[<p>I&rsquo;ve just come across <a href="http://www.eweek.com/c/a/Application-Development/Groovy-JavaScript-Ruby-Among-Fastest-Growing-Programming-Languages-505803/">eWeek&rsquo;s latest article on programming languages</a> trends, and they announce that Groovy is the fastest growing language!</p>
<blockquote>
<p>The data shows the percentage of jobs that have increased (or decreased) for various programming languages since November 2009. In the time frame this slide show depicts, Groovy saw the largest increase in jobs, followed by C# and—surprisingly—COBOL.</p></blockquote>
<p>Of course, we still have a lot of work to do to continue growing, and stay the most popular and successful alternative language for the JVM, but it&rsquo;s great to see such confirmations on a famous online business journal.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>The many ways to lace your shoes</title><link>https://glaforge.dev/posts/2011/09/07/the-many-ways-to-lace-your-shoes/</link><pubDate>Wed, 07 Sep 2011 02:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/09/07/the-many-ways-to-lace-your-shoes/</guid><description>&lt;p>In my feed reader, I came across this nice image showing the &lt;a href="http://pics.kuvaton.com/kuvei/17_ways.jpg">many ways to lace your shoes&lt;/a>!&lt;br />
I selected one of those that I&amp;rsquo;d like to try some day, it&amp;rsquo;s very nice looking:&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-877751cbe7be3ead721922f6d51049c5">
&lt;img src="https://glaforge.dev/img/misc/lace-your-shoes.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-877751cbe7be3ead721922f6d51049c5">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/lace-your-shoes.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>I think I&amp;rsquo;ve been using essentially the 4th and 7th for years.&lt;/p>
&lt;p>Which lacing scheme do you use?&lt;/p>
&lt;p>&lt;strong>Update:&lt;/strong> There are even &lt;a href="http://www.fieggen.com/shoelace/index.htm">sites dedicated to shoe lacing&lt;/a>!&lt;/p></description><content:encoded>
<![CDATA[<p>In my feed reader, I came across this nice image showing the <a href="http://pics.kuvaton.com/kuvei/17_ways.jpg">many ways to lace your shoes</a>!<br />
I selected one of those that I&rsquo;d like to try some day, it&rsquo;s very nice looking:</p>
<p><figure>
  <a href="#img-877751cbe7be3ead721922f6d51049c5">
    <img src="/img/misc/lace-your-shoes.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-877751cbe7be3ead721922f6d51049c5">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/lace-your-shoes.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I think I&rsquo;ve been using essentially the 4th and 7th for years.</p>
<p>Which lacing scheme do you use?</p>
<p><strong>Update:</strong> There are even <a href="http://www.fieggen.com/shoelace/index.htm">sites dedicated to shoe lacing</a>!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A new skin for the Groovy website</title><link>https://glaforge.dev/posts/2011/09/07/a-new-skin-for-the-groovy-website/</link><pubDate>Wed, 07 Sep 2011 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/09/07/a-new-skin-for-the-groovy-website/</guid><description>&lt;p>In the coming months, the Groovy team will be working on a nicer website with a web designer and better and up-to-date documentation. Obviously, this will take quite some time (especially for the content, more than for the look), but in the meantime, I wanted to refresh the look of the &lt;a href="http://groovy.codehaus.org">Groovy website&lt;/a> a bit. So I spent several hours today and tonight playing with CSS3!&lt;/p>
&lt;p>I used Twitter&amp;rsquo;s &lt;a href="http://twitter.github.com/bootstrap/">Bootstrap grid system&lt;/a> which is quite neat. Not only does it provides an easy to use grid system, but it also takes care of typography, menus, and much more. And I used &lt;a href="http://softwaremaniacs.org/soft/highlight/en/">highlight.js&lt;/a> for the Groovy syntax highlighting (the same that I use on this blog).&lt;/p></description><content:encoded>
<![CDATA[<p>In the coming months, the Groovy team will be working on a nicer website with a web designer and better and up-to-date documentation. Obviously, this will take quite some time (especially for the content, more than for the look), but in the meantime, I wanted to refresh the look of the <a href="http://groovy.codehaus.org">Groovy website</a> a bit. So I spent several hours today and tonight playing with CSS3!</p>
<p>I used Twitter&rsquo;s <a href="http://twitter.github.com/bootstrap/">Bootstrap grid system</a> which is quite neat. Not only does it provides an easy to use grid system, but it also takes care of typography, menus, and much more. And I used <a href="http://softwaremaniacs.org/soft/highlight/en/">highlight.js</a> for the Groovy syntax highlighting (the same that I use on this blog).</p>
<p>And the result looks like this now:</p>
<p><figure>
  <a href="#img-2946d0fa5effaafe2dbc847cd548c739">
    <img src="/img/misc/new-groovy-skin.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-2946d0fa5effaafe2dbc847cd548c739">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/new-groovy-skin.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Disclaimer: I&rsquo;m not a profesional web designer!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 1.8.2 and 1.9-beta-3 are in the wild</title><link>https://glaforge.dev/posts/2011/09/05/groovy-1-8-2-and-1-9-beta-3-are-in-the-wild/</link><pubDate>Mon, 05 Sep 2011 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/09/05/groovy-1-8-2-and-1-9-beta-3-are-in-the-wild/</guid><description>&lt;p>This is with great pleasure that the Groovy development team announces the joint releases of Groovy 1.8.2 and 1.9-beta-3. The big highlight of this release is the completion of the primitive type arithmetics performance optimizations. Microbenchmarks affictionados should be happy, and those wanting to do some number crunching should see better results with these improvements. Other than that, those two releases are essentially about bug fixes.&lt;/p>
&lt;p>You can have a look at the release notes here: &lt;a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;version=17494">http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;version=17494&lt;/a>&lt;/p></description><content:encoded>
<![CDATA[<p>This is with great pleasure that the Groovy development team announces the joint releases of Groovy 1.8.2 and 1.9-beta-3. The big highlight of this release is the completion of the primitive type arithmetics performance optimizations. Microbenchmarks affictionados should be happy, and those wanting to do some number crunching should see better results with these improvements. Other than that, those two releases are essentially about bug fixes.</p>
<p>You can have a look at the release notes here: <a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=17494">http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;version=17494</a></p>
<p>And you can download Groovy 1.8.2 and 1.9-beta-3 on the download page: <a href="http://groovy.codehaus.org/Download">http://groovy.codehaus.org/Download</a></p>
<p>In other news, thanks to Git expert <a href="http://ambientideas.com/">Matthew McCullough</a>, we completed our complex migration to Git. You can learn about the details to access and clone the Git repo here: <a href="http://xircles.codehaus.org/projects/groovy/repo/git/repo">http://xircles.codehaus.org/projects/groovy/repo/git/repo</a> Later on, we&rsquo;ll also provide a mirror on GitHub.</p>
<p>Small remark on the new beta of the 1.9 branch: due to a glitch a beta-2 was accidentally released in sync&rsquo;ed with Maven Central, so we bumped the beta number to 3, rather than sticking with 2, as Central won&rsquo;t allow us to override an existing version.</p>
<p>Thank you everybody for your feedback and contributions to those two releases.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Google App Engine's new pricing model</title><link>https://glaforge.dev/posts/2011/09/01/google-app-engine-s-new-pricing-model/</link><pubDate>Thu, 01 Sep 2011 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/09/01/google-app-engine-s-new-pricing-model/</guid><description>&lt;p>I&amp;rsquo;m quite disappointed by &lt;a href="http://code.google.com/appengine/">Google App Engine&lt;/a>&amp;rsquo;s new &lt;a href="http://www.google.com/enterprise/cloud/appengine/pricing.html">pricing model&lt;/a>.&lt;/p>
&lt;p>I was aware of the changes, the upcoming prices and quotas, but I wasn&amp;rsquo;t expecting my small low-trafic apps to go beyond the free quotas, and force me to have to pay for those small &lt;a href="http://gaelyk.appspot.com">Gaelyk&lt;/a> apps!&lt;/p>
&lt;p>The big problem is the cost of the &amp;ldquo;frontend instance hours&amp;rdquo;. An app running all the time, with low trafic, but enough to keep a frontend instance running all day will cost you 30 bucks a month with this new pricing policy.&lt;/p></description><content:encoded>
<![CDATA[<p>I&rsquo;m quite disappointed by <a href="http://code.google.com/appengine/">Google App Engine</a>&rsquo;s new <a href="http://www.google.com/enterprise/cloud/appengine/pricing.html">pricing model</a>.</p>
<p>I was aware of the changes, the upcoming prices and quotas, but I wasn&rsquo;t expecting my small low-trafic apps to go beyond the free quotas, and force me to have to pay for those small <a href="http://gaelyk.appspot.com">Gaelyk</a> apps!</p>
<p>The big problem is the cost of the &ldquo;frontend instance hours&rdquo;. An app running all the time, with low trafic, but enough to keep a frontend instance running all day will cost you 30 bucks a month with this new pricing policy.</p>
<p>Let me show you an example, of a small Gaelyk application: the <a href="http://groovyconsole.appspot.com">Groovy Web Console</a>. It&rsquo;s a simple application that features an editor to author some Groovy scripts, that you can then share publicly with others.</p>
<p>So far, the billing / quota details would show me the following results:</p>
<p><figure>
  <a href="#img-082844ae36eb880809dd18b28f3e086b">
    <img src="/img/gae-pricing/before-gae-pricing-change.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-082844ae36eb880809dd18b28f3e086b">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gae-pricing/before-gae-pricing-change.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>This application has always been below the free quotas, and even if the CPU time was a bit high, it was only half away from the free quota of CPU usage. So all was fine. Now, with the new pricing model, here&rsquo;s what you get:</p>
<p><figure>
  <a href="#img-0ac8ce8e1bea22118b2ca431daafafa8">
    <img src="/img/gae-pricing/after-gae-pricing-change.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-0ac8ce8e1bea22118b2ca431daafafa8">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/gae-pricing/after-gae-pricing-change.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I&rsquo;d be paying $1.43 for that specific day, because of the always-on frontend instance, as well as the datastore reads, which have become also quite expensive as well. So if I&rsquo;d have to pay one or two dollars a day, resulting in 30 or 60 bucks a month.</p>
<p>And the worst thing is that it&rsquo;s just a transitional step, as the screenshot shows, the frontend instance hours are currently at 50% their end price till November, so the cost will almost double.</p>
<p>As soon as your app will be up all the time, even with low trafic, you&rsquo;ll easily get to 
  <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>30</mn><mi>a</mi><mi>m</mi><mi>o</mi><mi>n</mi><mi>t</mi><mi>h</mi><mi mathvariant="normal">.</mi><mi>I</mi><mi>t</mi><mi>h</mi><mi>i</mi><mi>n</mi><mi>k</mi><mi>t</mi><mi>h</mi><mi>a</mi><msup><mi>t</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mi>s</mi><mi>o</mi><mi>n</mi><mi>p</mi><mi>u</mi><mi>r</mi><mi>p</mi><mi>o</mi><mi>s</mi><mi>e</mi><mo separator="true">,</mo><mi>t</mi><mi>o</mi><mi>f</mi><mi>o</mi><mi>r</mi><mi>c</mi><mi>e</mi><mi>u</mi><mi>s</mi><mi>e</mi><mi>r</mi><mi>s</mi><mi>t</mi><mi>o</mi><mi>g</mi><mi>o</mi><mi>w</mi><mi>i</mi><mi>t</mi><mi>h</mi><mi>t</mi><mi>h</mi><mi>e</mi><mi>f</mi><mi>i</mi><mi>r</mi><mi>s</mi><mi>t</mi><mi>p</mi><mi>a</mi><mi>i</mi><mi>d</mi><mi>t</mi><mi>i</mi><mi>e</mi><mi>r</mi><mi>o</mi><mi>f</mi><mi>t</mi><mi>h</mi><mi>e</mi><mo stretchy="false">[</mo><mi>n</mi><mi>e</mi><mi>w</mi><mi>p</mi><mi>r</mi><mi>i</mi><mi>c</mi><mi>i</mi><mi>n</mi><mi>g</mi><mi>m</mi><mi>o</mi><mi>d</mi><mi>e</mi><mi>l</mi><mo stretchy="false">]</mo><mo stretchy="false">(</mo><mi>h</mi><mi>t</mi><mi>t</mi><mi>p</mi><mo>:</mo><mi mathvariant="normal">/</mi><mi mathvariant="normal">/</mi><mi>w</mi><mi>w</mi><mi>w</mi><mi mathvariant="normal">.</mi><mi>g</mi><mi>o</mi><mi>o</mi><mi>g</mi><mi>l</mi><mi>e</mi><mi mathvariant="normal">.</mi><mi>c</mi><mi>o</mi><mi>m</mi><mi mathvariant="normal">/</mi><mi>e</mi><mi>n</mi><mi>t</mi><mi>e</mi><mi>r</mi><mi>p</mi><mi>r</mi><mi>i</mi><mi>s</mi><mi>e</mi><mi mathvariant="normal">/</mi><mi>c</mi><mi>l</mi><mi>o</mi><mi>u</mi><mi>d</mi><mi mathvariant="normal">/</mi><mi>a</mi><mi>p</mi><mi>p</mi><mi>e</mi><mi>n</mi><mi>g</mi><mi>i</mi><mi>n</mi><mi>e</mi><mi mathvariant="normal">/</mi><mi>p</mi><mi>r</mi><mi>i</mi><mi>c</mi><mi>i</mi><mi>n</mi><mi>g</mi><mi mathvariant="normal">.</mi><mi>h</mi><mi>t</mi><mi>m</mi><mi>l</mi><mo stretchy="false">)</mo><mi mathvariant="normal">.</mi><mi>T</mi><mi>o</mi><mi>a</mi><mi>v</mi><mi>o</mi><mi>i</mi><mi>d</mi><mi>t</mi><mi>h</mi><mi>o</mi><mi>s</mi><mi>e</mi><mi>o</mi><mi>u</mi><mi>t</mi><mi>r</mi><mi>a</mi><mi>g</mi><mi>e</mi><mi>o</mi><mi>u</mi><mi>s</mi><mi>c</mi><mi>o</mi><mi>s</mi><mi>t</mi><mo separator="true">,</mo><mi>y</mi><mi>o</mi><msup><mi>u</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mi>l</mi><mi>l</mi><mi>h</mi><mi>a</mi><mi>v</mi><mi>e</mi><mi>t</mi><mi>o</mi><mi>g</mi><mi>o</mi><mi>w</mi><mi>i</mi><mi>t</mi><mi>h</mi><mi>t</mi><mi>h</mi><mi>e</mi></mrow><annotation encoding="application/x-tex">30 a month. I think that&#x27;s on purpose, to force users to go with the first paid tier of the [new pricing model](http://www.google.com/enterprise/cloud/appengine/pricing.html). To avoid those outrageous cost, you&#x27;ll have to go with the </annotation></semantics></math></span>

9 / month plan, and you will be far from reaching the quotas included in that plan, so you&rsquo;ll pay really $9 even if you only use a dollar or two. Nine bucks is better than 30 or 60, but it still sounds expensive for me for a small blog, and a small utility app that are free content or services.</p>
<p>Really disappointed. And I&rsquo;m now wondering what I shall be doing with my brand new blog, and the Groovy web console&hellip; Even putting ads all over the place won&rsquo;t pay for the fees&hellip;</p>
<p>Now I hope that the <a href="http://cloudfoundry.org/">Cloud Foundry</a> model will be more friendly for small personal apps!</p>
<p>You can read more on the outraged comments here:</p>
<ul>
<li>a guy saying he&rsquo;ll <a href="http://groups.google.com/group/google-appengine/browse_thread/thread/76908543757c0507#">leave the plaform</a></li>
<li>the thread on the <a href="http://groups.google.com/group/google-appengine/browse_thread/thread/a1b7c68db2243932#">GAE google group</a></li>
<li>a <a href="https://plus.google.com/104961845171318028721/posts/DamjzZBVxd7">Google+ post from Russel Beattie</a></li>
<li>another post about the <a href="http://srirangan.net/2011-09-google-faces-backlash-for-new-app-engine-pricing">backlash over the pricing</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>CamelCaseCon Groovy conference in Germany</title><link>https://glaforge.dev/posts/2011/08/31/camelcasecon-groovy-conference-in-germany/</link><pubDate>Wed, 31 Aug 2011 10:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/08/31/camelcasecon-groovy-conference-in-germany/</guid><description>&lt;p>If you&amp;rsquo;re in Germany, don&amp;rsquo;t miss the &lt;a href="http://www.camelcasecon.de/">CamelCaseCon&lt;/a> conference in Düsseldorf! A conference dedicated to the Groovy ecosystem: &lt;a href="http://groovy.codehaus.org">Groovy&lt;/a>, &lt;a href="http://grails.org">Grails&lt;/a>, &lt;a href="http://gradle.org">Gradle&lt;/a>, &lt;a href="http://codenarc.sourceforge.net">CodeNarc&lt;/a> and &lt;a href="http://spockframework.org">Spock&lt;/a>.&lt;br />
Dierk König, Hans Dokter, Peter Niederwieser, Joachim Bauman, Stefan Glase, will cover all these Groovy-related topics, so you&amp;rsquo;ll hear the latest news from renowned experts.&lt;/p></description><content:encoded>
<![CDATA[<p>If you&rsquo;re in Germany, don&rsquo;t miss the <a href="http://www.camelcasecon.de/">CamelCaseCon</a> conference in Düsseldorf! A conference dedicated to the Groovy ecosystem: <a href="http://groovy.codehaus.org">Groovy</a>, <a href="http://grails.org">Grails</a>, <a href="http://gradle.org">Gradle</a>, <a href="http://codenarc.sourceforge.net">CodeNarc</a> and <a href="http://spockframework.org">Spock</a>.<br />
Dierk König, Hans Dokter, Peter Niederwieser, Joachim Bauman, Stefan Glase, will cover all these Groovy-related topics, so you&rsquo;ll hear the latest news from renowned experts.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>On CloudFoundry micro-cloud</title><link>https://glaforge.dev/posts/2011/08/31/on-cloudfoundry-micro-cloud/</link><pubDate>Wed, 31 Aug 2011 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/08/31/on-cloudfoundry-micro-cloud/</guid><description>&lt;p>VMware released the announced &lt;a href="http://blog.cloudfoundry.com/post/9331377393/we-shrunk-the-cloud-introducing-micro-cloud-foundry">CloudFoundry micro-cloud&lt;/a>.&lt;br />
First, what is that micro-cloud about? From the announcement on the website:&lt;/p>
&lt;blockquote>
&lt;p>Micro Cloud Foundry is a complete version of Cloud Foundry that runs in a virtual machine on a developer’s Mac or PC. It is a full instance of Cloud Foundry that provides the flexibility of local development while preserving your options for future deployment and scaling of your applications.&lt;/p>&lt;/blockquote>
&lt;p>I&amp;rsquo;m quite happy that this virtualized version of the real CloudFoundry is available, as this will greatly simplify the process for developing and testing your application locally.&lt;/p></description><content:encoded>
<![CDATA[<p>VMware released the announced <a href="http://blog.cloudfoundry.com/post/9331377393/we-shrunk-the-cloud-introducing-micro-cloud-foundry">CloudFoundry micro-cloud</a>.<br />
First, what is that micro-cloud about? From the announcement on the website:</p>
<blockquote>
<p>Micro Cloud Foundry is a complete version of Cloud Foundry that runs in a virtual machine on a developer’s Mac or PC. It is a full instance of Cloud Foundry that provides the flexibility of local development while preserving your options for future deployment and scaling of your applications.</p></blockquote>
<p>I&rsquo;m quite happy that this virtualized version of the real CloudFoundry is available, as this will greatly simplify the process for developing and testing your application locally.</p>
<p>One of the big pains I&rsquo;ve had with an environment like <a href="http://code.google.com/appengine/">Google App Engine</a> when developing <a href="http://gaelyk.appspot.com">Gaelyk</a> is that the local development environment is not really an image of the production server. So there are various differences between the two environments that lead you to wasting tons of precious hours figuring out why something works locally, but not in production, and vice versa, sometimes things work in production but not locally! I think an virtual image should reflect way more the original environment and smooth out most differences that could lie around.</p>
<p>I haven&rsquo;t downloaded it yet, but I&rsquo;m impatient to try micro-cloud soon!</p>
<p>Make sure to read Peter Ledbrook&rsquo;s article on <a href="http://blog.springsource.com/2011/08/24/using-micro-cloud-foundry-from-grails/">Grails on micro-CloudFoundry</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Already 8 years of Groovy!</title><link>https://glaforge.dev/posts/2011/08/29/already-8-years-of-groovy/</link><pubDate>Mon, 29 Aug 2011 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/08/29/already-8-years-of-groovy/</guid><description>&lt;p>Today is the 8th birthday of the &lt;a href="http://groovy.codehaus.org">Groovy dynamic language&lt;/a> project!&lt;/p>
&lt;p>I hardly believe I&amp;rsquo;ve been involved with the project for almost as long! I started contributing to the project very early, but only became committer 4 months or so later. It&amp;rsquo;s been such a great fun, great ride, meeting all those great persons, and seeing the community forming around the project, and further along, the wealth of satellite projects!&lt;/p>
&lt;p>There are still so many good things ahead of us, but we already have a solid, healthy, successful and popular project project, and that&amp;rsquo;s also thanks to all our users, and those who contributed in a way or another. So thanks to you all for all those great years!&lt;/p></description><content:encoded>
<![CDATA[<p>Today is the 8th birthday of the <a href="http://groovy.codehaus.org">Groovy dynamic language</a> project!</p>
<p>I hardly believe I&rsquo;ve been involved with the project for almost as long! I started contributing to the project very early, but only became committer 4 months or so later. It&rsquo;s been such a great fun, great ride, meeting all those great persons, and seeing the community forming around the project, and further along, the wealth of satellite projects!</p>
<p>There are still so many good things ahead of us, but we already have a solid, healthy, successful and popular project project, and that&rsquo;s also thanks to all our users, and those who contributed in a way or another. So thanks to you all for all those great years!</p>
<p>Keep on groovy&rsquo;ing!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Gaelyk 1.0 is out</title><link>https://glaforge.dev/posts/2011/08/08/gaelyk-1-0-is-out/</link><pubDate>Mon, 08 Aug 2011 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/08/08/gaelyk-1-0-is-out/</guid><description>&lt;p>I&amp;rsquo;m very happy to announce the final release of Gaelyk 1.0, the lightweight &lt;a href="http://groovy.codehaus.org">Groovy&lt;/a> toolkit for &lt;a href="http://code.google.com/appengine/">Google App Engine&lt;/a>!&lt;br />
It took some time to get to 1.0, but it&amp;rsquo;s now here, with the key feature I&amp;rsquo;ve been missing since the beginning of the project, in particular the &lt;a href="http://gaelyk.appspot.com/tutorial/app-engine-shortcuts#query">Query DSL&lt;/a> which allows you to write queries like this one:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-groovy" data-lang="groovy">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#902000">def&lt;/span> latestArticles &lt;span style="color:#666">=&lt;/span> datastore&lt;span style="color:#666">.&lt;/span>&lt;span style="color:#4070a0">execute&lt;/span> &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> select all from articles
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sort desc by dateCreated
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> where author &lt;span style="color:#666">==&lt;/span> params&lt;span style="color:#666">.&lt;/span>&lt;span style="color:#4070a0">author&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> limit &lt;span style="color:#40a070">10&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If you want to learn more about this lightweight framework, head over to: &lt;a href="http://gaelyk.appspot.com/">http://gaelyk.appspot.com/&lt;/a>&lt;br />
You can learn everything through the &lt;a href="http://gaelyk.appspot.com/tutorial">tutorial&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>I&rsquo;m very happy to announce the final release of Gaelyk 1.0, the lightweight <a href="http://groovy.codehaus.org">Groovy</a> toolkit for <a href="http://code.google.com/appengine/">Google App Engine</a>!<br />
It took some time to get to 1.0, but it&rsquo;s now here, with the key feature I&rsquo;ve been missing since the beginning of the project, in particular the <a href="http://gaelyk.appspot.com/tutorial/app-engine-shortcuts#query">Query DSL</a> which allows you to write queries like this one:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> latestArticles <span style="color:#666">=</span> datastore<span style="color:#666">.</span><span style="color:#4070a0">execute</span> <span style="color:#666">{</span>  
</span></span><span style="display:flex;"><span>    select all from articles  
</span></span><span style="display:flex;"><span>    sort desc by dateCreated  
</span></span><span style="display:flex;"><span>    where author <span style="color:#666">==</span> params<span style="color:#666">.</span><span style="color:#4070a0">author</span>  
</span></span><span style="display:flex;"><span>    limit <span style="color:#40a070">10</span>  
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>If you want to learn more about this lightweight framework, head over to: <a href="http://gaelyk.appspot.com/">http://gaelyk.appspot.com/</a><br />
You can learn everything through the <a href="http://gaelyk.appspot.com/tutorial">tutorial</a>.</p>
<p>And <a href="http://gaelyk.appspot.com/download">download the JAR or the template project</a>.</p>
<p>Should you want to ask questions on the <a href="http://groups.google.com/group/gaelyk">Google Groups</a>, get involved in the community, participate in the <a href="https://github.com/glaforge/gaelyk">development of this Open Source project</a>, have a look at our <a href="http://gaelyk.appspot.com/community">community page</a>.</p>
<p>This release wouldn&rsquo;t have been possible with all the great contributions of you all, your pull requests on <a href="https://github.com/glaforge/gaelyk">GitHub</a>, your Gradle plugins, your bug reports, your questions on the Google Group, etc. So I&rsquo;d like to tell you all a big thank you for all your work and contributions. They were very much appreciated, and keep them coming!<br />
The change log:</p>
<ul>
<li>GAE SDK updated to 1.5.2 and Groovy to 1.8.1</li>
<li>Introduction of a <a href="http://gaelyk.appspot.com/tutorial/app-engine-shortcuts#query">Query DSL for creating SQL-like queries</a> against the datastore</li>
<li>Updated <a href="http://gaelyk.appspot.com/tutorial/template-project">template project</a> with a Gradle build, the usage of Gradle GAE / Gaelyk plugins, and the support of Spock for testing Groovlets</li>
<li>Introduction of the <a href="http://gaelyk.appspot.com/plugins">plugins page</a> in the Gaelyk website, for referencing known plugins</li>
<li>By annotating classes with GaelykBindings, the same <a href="http://gaelyk.appspot.com/tutorial/views-and-controllers#gaelykBindings">services and variables are injected in your classes as properties</a>, as the ones which are injected in Groovlets and templates</li>
<li>The <a href="http://gaelyk.appspot.com/tutorial/url-routing#path-variable-validation">validation closures</a> of the routes in your URL mapping have access to the request, so you can validate a URL depending on what&rsquo;s in your request (attribute, session, etc.)</li>
<li>Added a DSLD file (DSL descriptor) for Eclipse for easing code-completion and navigation</li>
<li>Added a <a href="http://gaelyk.appspot.com/tutorial/app-engine-shortcuts#delete-get-on-key">get() method on Key</a>, as well as on lists of keys</li>
<li>Ability to convert <a href="http://gaelyk.appspot.com/tutorial/app-engine-shortcuts#list-to-key-coercion">lists to Keys</a></li>
<li>Added two encoded <a href="http://gaelyk.appspot.com/tutorial/app-engine-shortcuts#key-string-conversion">string and key conversion</a> utilities</li>
<li><a href="http://gaelyk.appspot.com/tutorial/app-engine-shortcuts#datastore-get">Additional datastore.get() methods</a> to retrieve entities by their keys more concisely</li>
<li>Problems with the recent XMPP support fixed</li>
<li>Fixed inability to access the various services and variables from within binding/before/after blocks in plugin descriptors</li>
</ul>
<p>Additionally, this very blog post you are now reading is actually powered by a new sample app, the bloogaey blog engine, developed with Gaelyk 1.0.</p>
<p>If you&rsquo;re interested in this blog engine, head over to the <a href="https://github.com/glaforge/bloogaey">bloogaey project on GitHub</a>. You&rsquo;re welcome to fork it to suit your needs, and to help improving it with pull requests!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Blog.reboot()</title><link>https://glaforge.dev/posts/2011/07/26/blog-reboot/</link><pubDate>Tue, 26 Jul 2011 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/07/26/blog-reboot/</guid><description>&lt;p>It was time I rebooted my old blog! It used to be in PHP, and it&amp;rsquo;s time to move to the next millenium with a blog engine written in &lt;a href="http://groovy.codehaus.org">Groovy&lt;/a>, using the &lt;a href="http://gaelyk.appspot.com">Gaelyk&lt;/a> lightweight toolkit, and deployed on &lt;a href="http://code.google.com/appengine/">Google App Engine&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>It was time I rebooted my old blog! It used to be in PHP, and it&rsquo;s time to move to the next millenium with a blog engine written in <a href="http://groovy.codehaus.org">Groovy</a>, using the <a href="http://gaelyk.appspot.com">Gaelyk</a> lightweight toolkit, and deployed on <a href="http://code.google.com/appengine/">Google App Engine</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 1.8-final is out the door!</title><link>https://glaforge.dev/posts/2011/04/27/groovy-1-8-final-is-out-the-door/</link><pubDate>Wed, 27 Apr 2011 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/04/27/groovy-1-8-final-is-out-the-door/</guid><description>&lt;p>The Groovy development team is really pleased and proud to announce the release of the &lt;strong>final version of Groovy 1.8.0&lt;/strong>!&lt;/p>
&lt;p>After a lot of work and efforts throughout four betas and four release candidates, version 1.8 of Groovy has been long in the making, but is packed with &lt;strong>tons of new features and enhancements&lt;/strong>, for your productivity, and your pleasure. In particular, you&amp;rsquo;ll be happy to learn about:&lt;/p>
&lt;ul>
&lt;li>the new &lt;a href="http://bit.ly/groovy-18#Groovy1.8releasenotes-CommandchainsfornicerDomainSpecificLanguages">Domain-Specific Language authoring capabilities&lt;/a> for more readability and expressivity of your business rules,&lt;/li>
&lt;li>the &lt;a href="http://bit.ly/groovy-18#Groovy1.8releasenotes-Performanceimprovements">runtime performance improvements&lt;/a>,&lt;/li>
&lt;li>the &lt;a href="http://bit.ly/groovy-18#Groovy1.8releasenotes-GParsbundledwithintheGroovydistribution">bundling of the GPars parallel and concurrency library&lt;/a>,&lt;/li>
&lt;li>the &lt;a href="http://bit.ly/groovy-18#Groovy1.8releasenotes-NativeJSONsupport">built-in JSON support&lt;/a>,&lt;/li>
&lt;li>the new &lt;a href="http://bit.ly/groovy-18#Groovy1.8releasenotes-NewASTTransformations">compile-time meta-programming features&lt;/a> (several new useful AST transformations),&lt;/li>
&lt;li>the new &lt;a href="http://bit.ly/groovy-18#Groovy1.8releasenotes-Closureenhancements">functional programming aspects of closures&lt;/a>,&lt;/li>
&lt;li>and much more.&lt;/li>
&lt;/ul>
&lt;p>To get all the details, with code samples, we have prepared an in-depth &lt;a href="http://bit.ly/groovy-18">release notes&lt;/a> document. Please have a look at it to learn more about the features listed above, and discover other smaller enhancements as well.&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy development team is really pleased and proud to announce the release of the <strong>final version of Groovy 1.8.0</strong>!</p>
<p>After a lot of work and efforts throughout four betas and four release candidates, version 1.8 of Groovy has been long in the making, but is packed with <strong>tons of new features and enhancements</strong>, for your productivity, and your pleasure. In particular, you&rsquo;ll be happy to learn about:</p>
<ul>
<li>the new <a href="http://bit.ly/groovy-18#Groovy1.8releasenotes-CommandchainsfornicerDomainSpecificLanguages">Domain-Specific Language authoring capabilities</a> for more readability and expressivity of your business rules,</li>
<li>the <a href="http://bit.ly/groovy-18#Groovy1.8releasenotes-Performanceimprovements">runtime performance improvements</a>,</li>
<li>the <a href="http://bit.ly/groovy-18#Groovy1.8releasenotes-GParsbundledwithintheGroovydistribution">bundling of the GPars parallel and concurrency library</a>,</li>
<li>the <a href="http://bit.ly/groovy-18#Groovy1.8releasenotes-NativeJSONsupport">built-in JSON support</a>,</li>
<li>the new <a href="http://bit.ly/groovy-18#Groovy1.8releasenotes-NewASTTransformations">compile-time meta-programming features</a> (several new useful AST transformations),</li>
<li>the new <a href="http://bit.ly/groovy-18#Groovy1.8releasenotes-Closureenhancements">functional programming aspects of closures</a>,</li>
<li>and much more.</li>
</ul>
<p>To get all the details, with code samples, we have prepared an in-depth <a href="http://bit.ly/groovy-18">release notes</a> document. Please have a look at it to learn more about the features listed above, and discover other smaller enhancements as well.</p>
<p>You can download Groovy 1.8 in our <a href="http://groovy.codehaus.org/Download%3Edownload%20section%3C/a%3E%C2%A0and%20you%20can%20have%20a%20look%20at%20the%20list%20of%20%3Ca%20href=">JIRA tickets</a> that have found their way into this major release.</p>
<p>We&rsquo;d like to thank all those who participated and contributed to this release: users, contributors, committers, framework writers, IDE developers, book authors. Without you all, Groovy wouldn&rsquo;t be the great productive language it is now. And again, without you all, Groovy wouldn&rsquo;t be surrounded by its <strong>vibrant, active and rich ecosystem</strong>, giving you advanced tools and frameworks for building web applications (<a href="http://grails.org/">Grails</a>, <a href="http://gaelyk.appspot.com/">Gaelyk</a>) or rich desktop applications (<a href="http://griffon.codehaus.org/">Griffon</a>), for building your own projects (<a href="http://www.gradle.org/">Gradle</a>), for testing your projects (<a href="http://spockframework.org/">Spock</a>, <a href="http://geb.codehaus.org/">Geb</a>), for tackling the concurrency and parallel problems on our multi-core / multi-processor architectures (<a href="http://gpars.codehaus.org/">GPars</a>), or for improving the quality of your Groovy code bases (<a href="http://codenarc.sourceforge.net/">CodeNarc</a> for static code analysis, <a href="https://github.com/andresteingress/gcontracts/wiki/">GContracts</a> for design by contract).</p>
<p>Enjoy this release!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>GR8Conf Europe 2011 - a conference dedicated to the Groovy Ecosystem</title><link>https://glaforge.dev/posts/2011/03/31/gr8conf-europe-2011-a-conference-dedicated-to-the-groovy-ecosystem/</link><pubDate>Thu, 31 Mar 2011 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2011/03/31/gr8conf-europe-2011-a-conference-dedicated-to-the-groovy-ecosystem/</guid><description>&lt;p>&lt;figure>
&lt;a href="#img-72f39b08d2bd6a7cebd91fd67ae40a07">
&lt;img src="http://www.eu2011.gr8conf.org/a/125x125_banner_animated.gif"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-72f39b08d2bd6a7cebd91fd67ae40a07">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="http://www.eu2011.gr8conf.org/a/125x125_banner_animated.gif"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
It is this time of the year: &lt;a href="http://gr8conf.eu/">GR8Conf Europe 2011&lt;/a> is ready to rock the Groovy world!&lt;/p>
&lt;p>&lt;a href="http://www.eu2011.gr8conf.org/register">Register now&lt;/a> to &lt;strong>save €300&lt;/strong> with the Early Bird rate!&lt;/p>
&lt;p>Three days packed with &lt;strong>Groovy, Grails, Griffon, Gradle, Spock, GPars, Gaelyk, AST transformations, GContracts, Grails Plugins development&lt;/strong>, and lots of other gr8 stuff.&lt;/p>
&lt;p>This year, the &lt;strong>conference is growing&lt;/strong>. We kick of the conference with a &lt;strong>university day&lt;/strong>, with two tracks, one intermediate and one advanced, with workshops and hands-on training:&lt;/p></description><content:encoded>
<![CDATA[<p><figure>
  <a href="#img-72f39b08d2bd6a7cebd91fd67ae40a07">
    <img src="http://www.eu2011.gr8conf.org/a/125x125_banner_animated.gif"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-72f39b08d2bd6a7cebd91fd67ae40a07">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="http://www.eu2011.gr8conf.org/a/125x125_banner_animated.gif"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
It is this time of the year: <a href="http://gr8conf.eu/">GR8Conf Europe 2011</a> is ready to rock the Groovy world!</p>
<p><a href="http://www.eu2011.gr8conf.org/register">Register now</a> to <strong>save €300</strong> with the Early Bird rate!</p>
<p>Three days packed with <strong>Groovy, Grails, Griffon, Gradle, Spock, GPars, Gaelyk, AST transformations, GContracts, Grails Plugins development</strong>, and lots of other gr8 stuff.</p>
<p>This year, the <strong>conference is growing</strong>. We kick of the conference with a <strong>university day</strong>, with two tracks, one intermediate and one advanced, with workshops and hands-on training:</p>
<ul>
<li>The intermediate will kickstart your Groovy and Grails development skills with a <strong>Groovy workshop</strong>, followed by a <strong>Grails workshop</strong>.</li>
<li>The advanced track will feature an <strong>AST transformations workshop</strong>, followed by a <strong>Grails plugin development workshop</strong>.</li>
</ul>
<p>Note: The university day is optional, if you only want to attende the two main conference days.</p>
<p>The <a href="http://www.eu2011.gr8conf.org/agenda/list">conference agenda</a> will feature updates from Groovy, Grails, Griffon, Gradle, Spock and others, by the founders and developers of these tools and frameworks.</p>
<p><strong>You will learn all about those groovy-powered project of the ecosystem from the horse’s mouth</strong>. If you want to meet and chat with guys like Guillaume Laforge, Jochen “blackdrag” Theodorou, Paul King, Dierk König and Hamlet D’Arcy (Groovy), like Andres Almiray (Griffon), like Peter Ledbrook and Burt Beckwith (Grails), like Peter Niederwieser (Gradle and Spock), or Václav Pech (GPars), and many more, this is the best opportunity for you to interact with the gang!</p>
<p>There will be <strong>case studies</strong> from various companies, telling about <strong>real life projects</strong>, and there will be lots of sessions covering various topics in the Groovy ecosystem. To cram this in to two days, the conference is going to be <strong>two-tracked</strong>, compared to the previous editions.</p>
<p>To top it off, we feature <a href="http://hackergarten.net/">Hackergarten</a> — an open event, where you can come and hack your favorite Open Source project. You can attend even if you are not a conference attendee!</p>
<p>The conference takes place in <strong>Copenhagen</strong>, Denmark, on <strong>May 17th-19th</strong>.</p>
<p>You can benefit from the interesting <strong>Early Bird</strong> rate until <strong>April 14th</strong> and <strong>save €300</strong>! That’s a bargain you shouldn’t miss!</p>
<p>Register at <a href="http://gr8conf.eu/">http://gr8conf.eu</a> now!</p>
<p>Søren Berg Glasius and myself are looking forward to meeting you there!</p>
<p>Follow GR8Conf on twitter: <a href="http://twitter.com/gr8conf">@gr8conf</a></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>New important milestone for Gaelyk, with version 0.6 released</title><link>https://glaforge.dev/posts/2011/01/08/new-important-milestone-for-gaelyk-with-version-0-6-released/</link><pubDate>Sat, 08 Jan 2011 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2011/01/08/new-important-milestone-for-gaelyk-with-version-0-6-released/</guid><description>&lt;p>I&amp;rsquo;m very pleased to announce the release of Gaelyk 0.6.&lt;/p>
&lt;p>This new version is a pretty significant milestone, both in terms of features and quality. We&amp;rsquo;re really approaching a final 1.0 version!&lt;/p>
&lt;p>Here&amp;rsquo;s a list of the novelties, updates, and bug fixes.&lt;/p>
&lt;ul>
&lt;li>Updated to GAE SDK 1.4.0 and Groovy 1.7.6&lt;/li>
&lt;li>Channel service added in the binding and added a convenient method for sending messages&lt;/li>
&lt;li>Ability to specify the &amp;ldquo;warmup request&amp;rdquo; handler through a route definition&lt;/li>
&lt;li>Added app.gaelyk.version in the binding&lt;/li>
&lt;li>Use a servlet context listener for initializing the plugin system&lt;/li>
&lt;li>Initial support for the asynchronous datastore&lt;/li>
&lt;li>Updated the task queue enhancements to use the new package (as task queues migrated from labs)&lt;/li>
&lt;li>Introduced a Gradle build script for building Gaelyk itself&lt;/li>
&lt;li>Increased the code coverage of the project to over 82%&lt;/li>
&lt;li>Added before{} request and after{} request lifecycle hooks to plugins&lt;/li>
&lt;li>Added initial Eclipse project files in the template project&lt;/li>
&lt;li>Fixed a bug with ignore URL routes which triggered NPEs after the capabilities routing was added&lt;/li>
&lt;li>Corrected typos in the tutorials&lt;/li>
&lt;/ul>
&lt;p>Be careful, however, as there are two breaking changes compared to previous versions:&lt;/p></description><content:encoded>
<![CDATA[<p>I&rsquo;m very pleased to announce the release of Gaelyk 0.6.</p>
<p>This new version is a pretty significant milestone, both in terms of features and quality. We&rsquo;re really approaching a final 1.0 version!</p>
<p>Here&rsquo;s a list of the novelties, updates, and bug fixes.</p>
<ul>
<li>Updated to GAE SDK 1.4.0 and Groovy 1.7.6</li>
<li>Channel service added in the binding and added a convenient method for sending messages</li>
<li>Ability to specify the &ldquo;warmup request&rdquo; handler through a route definition</li>
<li>Added app.gaelyk.version in the binding</li>
<li>Use a servlet context listener for initializing the plugin system</li>
<li>Initial support for the asynchronous datastore</li>
<li>Updated the task queue enhancements to use the new package (as task queues migrated from labs)</li>
<li>Introduced a Gradle build script for building Gaelyk itself</li>
<li>Increased the code coverage of the project to over 82%</li>
<li>Added before{} request and after{} request lifecycle hooks to plugins</li>
<li>Added initial Eclipse project files in the template project</li>
<li>Fixed a bug with ignore URL routes which triggered NPEs after the capabilities routing was added</li>
<li>Corrected typos in the tutorials</li>
</ul>
<p>Be careful, however, as there are two breaking changes compared to previous versions:</p>
<ul>
<li>Compared to the previous version of the toolkit, the handling of incoming emails and incoming jabber messages has changed. The GaelykIncomingEmailServlet and GaelykXmppServlet are gone. It is no longer required to have dedicated servlets for those two purposes, instead you must use the URL routing system to indicate the handlers that will take care of the incoming messages. If you were relying on those two servlets, please make sure to upgrade, and read the updated tutorial on URL routing and incoming email and jabber messages.</li>
<li>The initialization of the plugin system is not done anymore by the Groovlet and template servlet, but is done by a servlet context listener. So you&rsquo;ll have to update your web.xml file to specify that listener. Please have a look at the template project or the documentation on how to setup the new context listener.</li>
</ul>
<p>You will also notice that the Gaelyk website has been updated:</p>
<ul>
<li>You will find some &ldquo;quick links&rdquo; to go more directly to the information that matters.</li>
<li>On the front page, a list of a few live Gaelyk websites in the wild is displayed</li>
<li>A <a href="http://gaelyk.appspot.com/search">&ldquo;search&rdquo; section</a> has been implemented, using Google&rsquo;s custom search engine, which will let you search through the Gaelyk website, the GitHub content, as well as the Gaelyk Google Group messages</li>
<li>You now have a <a href="http://gaelyk.appspot.com/tutorial/print">single-page documentation</a> option for those who wish to print the documentation (please think about the trees before printing)</li>
<li>And a <a href="http://gaelyk.appspot.com/gaelyk.pdf">PDF of the whole documentation</a> is available, which is handy for offline browsing.</li>
</ul>
<p>Please download Gaelyk 0.6 here: <a href="http://gaelyk.appspot.com/download">http://gaelyk.appspot.com/download</a></p>
<p>Contribute to Gaelyk:</p>
<ul>
<li><a href="http://gaelyk.appspot.com/community#discuss">Discussions</a></li>
<li><a href="http://gaelyk.appspot.com/community#repository">Code repository</a></li>
<li><a href="http://gaelyk.appspot.com/community#bugtracker">Bug tracker</a></li>
</ul>
<p>Thanks a lot for your attention and enjoy!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Gaelyk 0.4.4 out with namespace/multitenancy support</title><link>https://glaforge.dev/posts/2010/09/10/gaelyk-0-4-4-out-with-namespace-multitenancy-support/</link><pubDate>Fri, 10 Sep 2010 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2010/09/10/gaelyk-0-4-4-out-with-namespace-multitenancy-support/</guid><description>&lt;p>I&amp;rsquo;m pleased to announce the release of Gaelyk 0.4.4!&lt;/p>
&lt;p>What&amp;rsquo;s new?&lt;/p>
&lt;ul>
&lt;li>Updated the Google App Engine SDK to the latest 1.3.7 version&lt;/li>
&lt;li>Jabber and incoming email groovlets now have their implicit logger (gaelyk.email and gaelyk.jabber)&lt;/li>
&lt;li>Plugins are now impacting Jabber and incoming email groovlets as well&lt;/li>
&lt;li>Fixed a bug the conversion of String to Datastore&amp;rsquo;s Category type&lt;/li>
&lt;li>Internal refactorings of the caching logic&lt;/li>
&lt;li>Added namespace support for multitenancy, added in SDK 1.3.7:
&lt;ul>
&lt;li>a namespace is added in the binding, pointing at NamespaceManager, the SDK class dealing with namespaces&lt;/li>
&lt;li>a new method &lt;code>namespace.of(&amp;quot;customerA&amp;quot;) { ... }&lt;/code> to execute a closure in the context of a specific namespace&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>Although I haven&amp;rsquo;t mentioned it in the notes above (on on the download page), some of the internal refactorings of the caching and routing logic have also helped solving problems with the blobstore support. Please report back to me if it&amp;rsquo;s working as expected, for text content as well as binary content stored in blobstore (the latter was problematic in 0.4.3).&lt;/p></description><content:encoded>
<![CDATA[<p>I&rsquo;m pleased to announce the release of Gaelyk 0.4.4!</p>
<p>What&rsquo;s new?</p>
<ul>
<li>Updated the Google App Engine SDK to the latest 1.3.7 version</li>
<li>Jabber and incoming email groovlets now have their implicit logger (gaelyk.email and gaelyk.jabber)</li>
<li>Plugins are now impacting Jabber and incoming email groovlets as well</li>
<li>Fixed a bug the conversion of String to Datastore&rsquo;s Category type</li>
<li>Internal refactorings of the caching logic</li>
<li>Added namespace support for multitenancy, added in SDK 1.3.7:
<ul>
<li>a namespace is added in the binding, pointing at NamespaceManager, the SDK class dealing with namespaces</li>
<li>a new method <code>namespace.of(&quot;customerA&quot;) { ... }</code> to execute a closure in the context of a specific namespace</li>
</ul>
</li>
</ul>
<p>Although I haven&rsquo;t mentioned it in the notes above (on on the download page), some of the internal refactorings of the caching and routing logic have also helped solving problems with the blobstore support. Please report back to me if it&rsquo;s working as expected, for text content as well as binary content stored in blobstore (the latter was problematic in 0.4.3).</p>
<p>Make sure to download the latest and greatest version of Gaelyk here: <a href="http://gaelyk.appspot.com/download">http://gaelyk.appspot.com/download</a></p>
<p>Thanks a lot to all those who contributed to this release!</p>
<p>I&rsquo;m still looking for some help for some acute eyes to see why we still have this dangling includes issue when mixing includes and cache. I still haven&rsquo;t found the cause, and would gladly accept help to find out what&rsquo;s going on! (I was hoping my internal refactorings would help, but I&rsquo;ve not succeeded so far)</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Nicer DSLs in Groovy 1.8 thanks to Extended Command Expressions</title><link>https://glaforge.dev/posts/2010/08/31/nicer-dsls-in-groovy-1-8-thanks-to-extended-command-expressions/</link><pubDate>Tue, 31 Aug 2010 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2010/08/31/nicer-dsls-in-groovy-1-8-thanks-to-extended-command-expressions/</guid><description>&lt;p>A quick heads-up to tell you about an upcoming Groovy 1.8 feature which will allow us to make nicer DSLs. This feature will be available in Groovy 1.8-beta-2, which will probably be released before JavaOne.&lt;/p>
&lt;p>Lidia Donajczyk was our Google Summer of Code student this year, working on the implementation of GEP-3, an extension to Groovy&amp;rsquo;s command expressions. You can have a look at the &lt;a href="http://docs.codehaus.org/display/GroovyJSR/GEP+3+-+Command+Expression+based+DSL">GEP-3 page&lt;/a> for the guiding ideas behind this enhancement proposal.&lt;/p></description><content:encoded>
<![CDATA[<p>A quick heads-up to tell you about an upcoming Groovy 1.8 feature which will allow us to make nicer DSLs. This feature will be available in Groovy 1.8-beta-2, which will probably be released before JavaOne.</p>
<p>Lidia Donajczyk was our Google Summer of Code student this year, working on the implementation of GEP-3, an extension to Groovy&rsquo;s command expressions. You can have a look at the <a href="http://docs.codehaus.org/display/GroovyJSR/GEP+3+-+Command+Expression+based+DSL">GEP-3 page</a> for the guiding ideas behind this enhancement proposal.</p>
<p>Just like you could use simple command expressions before, like:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>println <span style="color:#4070a0">&#34;hello&#34;</span>
</span></span><span style="display:flex;"><span>move left
</span></span><span style="display:flex;"><span>make coffee
</span></span><span style="display:flex;"><span>drink tea
</span></span></code></pre></div><p>GEP-3 extends this for more complex sentences such as:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>drink tea with sugar and milk  
</span></span><span style="display:flex;"><span>move left by <span style="color:#40a070">30</span><span style="color:#666">.</span><span style="color:#4070a0">centimeters</span>  
</span></span><span style="display:flex;"><span>sendFrom <span style="color:#4070a0">&#34;Guillaume&#34;</span> to <span style="color:#4070a0">&#34;Jochen&#34;</span>  
</span></span><span style="display:flex;"><span>send <span style="color:#002070;font-weight:bold">from:</span> <span style="color:#4070a0">&#34;Jochen&#34;</span> to <span style="color:#4070a0">&#34;Lidia&#34;</span>  
</span></span><span style="display:flex;"><span>Email<span style="color:#666">.</span><span style="color:#4070a0">from</span> <span style="color:#4070a0">&#34;Lidia&#34;</span> to <span style="color:#4070a0">&#34;Guillaume&#34;</span> withBody <span style="color:#4070a0">&#34;how are you?&#34;</span>  
</span></span><span style="display:flex;"><span>contact<span style="color:#666">.</span><span style="color:#4070a0">name</span> <span style="color:#4070a0">&#34;Guillaume&#34;</span> age <span style="color:#40a070">33</span>  
</span></span><span style="display:flex;"><span>move left by <span style="color:#40a070">30</span><span style="color:#666">.</span><span style="color:#4070a0">centimeters</span>  
</span></span><span style="display:flex;"><span>sell <span style="color:#40a070">100</span><span style="color:#666">.</span><span style="color:#4070a0">shares</span> of MSFT  
</span></span><span style="display:flex;"><span>take <span style="color:#40a070">2</span><span style="color:#666">.</span><span style="color:#4070a0">pills</span> of chloroquinine <span style="color:#007020;font-weight:bold">in</span> <span style="color:#40a070">6</span><span style="color:#666">.</span><span style="color:#4070a0">hours</span>  
</span></span><span style="display:flex;"><span>blend red<span style="color:#666">,</span> green of acrylic  
</span></span><span style="display:flex;"><span>artist<span style="color:#666">.</span><span style="color:#4070a0">paint</span> <span style="color:#4070a0">&#34;wall&#34;</span> with <span style="color:#4070a0">&#34;Red&#34;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#34;Green&#34;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">and:</span> <span style="color:#4070a0">&#34;Blue&#34;</span> at <span style="color:#40a070">3</span><span style="color:#666">.</span><span style="color:#4070a0">pm</span>  
</span></span><span style="display:flex;"><span>wait <span style="color:#40a070">2</span><span style="color:#666">.</span><span style="color:#4070a0">seconds</span> and execute <span style="color:#666">{</span> <span style="color:#007020;font-weight:bold">assert</span> <span style="color:#007020;font-weight:bold">true</span> <span style="color:#666">}</span>  
</span></span><span style="display:flex;"><span>concat arr<span style="">\</span><span style="color:#666">[</span><span style="color:#40a070">0</span><span style="">\</span><span style="color:#666">]</span> with arr<span style="">\</span><span style="color:#666">[</span><span style="color:#40a070">1</span><span style="">\</span><span style="color:#666">]</span> and arr<span style="">\</span><span style="color:#666">[</span><span style="color:#40a070">2</span><span style="">\</span><span style="color:#666">]</span>  
</span></span><span style="display:flex;"><span>developped <span style="color:#002070;font-weight:bold">with:</span> <span style="color:#4070a0">&#34;Groovy&#34;</span> version <span style="color:#4070a0">&#34;1.8-beta-2&#34;</span>
</span></span></code></pre></div><p>A more <a href="http://groovyconsole.appspot.com/script/214001">thourough example</a> is this:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>Recipe<span style="color:#666">.</span><span style="color:#4070a0">instructions</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    take medium_bowl
</span></span><span style="display:flex;"><span>    combine soy_sauce<span style="color:#666">,</span> vinegar<span style="color:#666">,</span> chili_powder<span style="color:#666">,</span> garlic
</span></span><span style="display:flex;"><span>    place chicken into sauce
</span></span><span style="display:flex;"><span>    turn once to coat
</span></span><span style="display:flex;"><span>    marinate <span style="color:#40a070">30</span><span style="color:#666">.</span><span style="color:#4070a0">minutes</span> at room_temperature
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><h2 id="summary-of-the-pattern">Summary of the pattern</h2>
<ul>
<li>A command-expression is composed of an even number of elements</li>
<li>The elements are alternating a method name, and its parameters (can be named and non-named parameters, and a mix of them)</li>
<li>A parameter element can be any kind of expression (ie. a method call <code>foo()</code>, <code>foo{}</code>, or some expression like <code>x+y</code>)</li>
<li>All those pairs of method name and parameters are actually chained method calls (ie. <code>send &quot;hello&quot; to &quot;Guillaume&quot;</code> is two methods chained one after the other as <code>send(&quot;hello&quot;).to(&quot;Guillaume&quot;))</code></li>
</ul>
<h2 id="current-limitations">Current limitations</h2>
<ul>
<li>Extended command expressions are not yet allowed on the RHS (righ-hand side) of assignments, so you can&rsquo;t yet do: <code>def movement = move left by 30.centimeters</code></li>
<li>Also, we can&rsquo;t yet use methods with zero-arguments.</li>
</ul>
<p>Hopefully, we&rsquo;ll work on those limitations before the final release of Groovy 1.8. One of the nice aspects of this extended command expression pattern is that it should play nicely with most Java APIs following the Java builder pattern.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>GR8Conf: the European conference dedicated to the Groovy ecosystem is back!</title><link>https://glaforge.dev/posts/2010/04/12/gr8conf-the-european-conference-dedicated-to-the-groovy-ecosystem-is-back/</link><pubDate>Mon, 12 Apr 2010 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2010/04/12/gr8conf-the-european-conference-dedicated-to-the-groovy-ecosystem-is-back/</guid><description>&lt;p>The &lt;a href="http://eu.gr8conf.org/eu2010/">GR8Conf&lt;/a> is back! A conference dedicated to &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a>, &lt;a href="http://grails.org/">Grails&lt;/a>, &lt;a href="http://griffon.codehaus.org/">Griffon&lt;/a>, &lt;a href="http://www.gradle.org/">Gradle&lt;/a>, &lt;a href="http://www.spockframework.org/">Spock&lt;/a> and more! This second edition of the conference &lt;a href="http://eu.gr8conf.org/eu2010/information/show/4">takes place in Copehagen, Denmark&lt;/a>, again like last year. And it&amp;rsquo;s solely &lt;strong>dedicated to the Groovy Ecosystem&lt;/strong>, and all the &lt;em>GR8&lt;/em> technologies based on it.&lt;/p>
&lt;p>Over &lt;a href="http://eu.gr8conf.org/eu2010/agenda/index">two days, in a single track&lt;/a> (to avoid missing important sessions because of too much choice), you&amp;rsquo;ll hear about:&lt;/p>
&lt;ul>
&lt;li>the &lt;strong>Groovy&lt;/strong> dynamic language for the JVM&lt;/li>
&lt;li>the &lt;strong>Grails&lt;/strong> web application stack based on Spring, Tomcat and Hibernate&lt;/li>
&lt;li>the &lt;strong>Griffon&lt;/strong> swing application framework for rich desktop applications&lt;/li>
&lt;li>the &lt;strong>&lt;a href="http://gaelyk.appspot.com/">Gaelyk&lt;/a>&lt;/strong> lightweight toolkit for easily developing and deploying small applications to Google App Engine&lt;/li>
&lt;li>the &lt;strong>Gradle&lt;/strong> build solution system&lt;/li>
&lt;li>the &lt;strong>GPars&lt;/strong> parallel library for Groovy&lt;/li>
&lt;/ul>
&lt;p>And some advanced talk on:&lt;/p></description><content:encoded>
<![CDATA[<p>The <a href="http://eu.gr8conf.org/eu2010/">GR8Conf</a> is back! A conference dedicated to <a href="http://groovy.codehaus.org/">Groovy</a>, <a href="http://grails.org/">Grails</a>, <a href="http://griffon.codehaus.org/">Griffon</a>, <a href="http://www.gradle.org/">Gradle</a>, <a href="http://www.spockframework.org/">Spock</a> and more! This second edition of the conference <a href="http://eu.gr8conf.org/eu2010/information/show/4">takes place in Copehagen, Denmark</a>, again like last year. And it&rsquo;s solely <strong>dedicated to the Groovy Ecosystem</strong>, and all the <em>GR8</em> technologies based on it.</p>
<p>Over <a href="http://eu.gr8conf.org/eu2010/agenda/index">two days, in a single track</a> (to avoid missing important sessions because of too much choice), you&rsquo;ll hear about:</p>
<ul>
<li>the <strong>Groovy</strong> dynamic language for the JVM</li>
<li>the <strong>Grails</strong> web application stack based on Spring, Tomcat and Hibernate</li>
<li>the <strong>Griffon</strong> swing application framework for rich desktop applications</li>
<li>the <strong><a href="http://gaelyk.appspot.com/">Gaelyk</a></strong> lightweight toolkit for easily developing and deploying small applications to Google App Engine</li>
<li>the <strong>Gradle</strong> build solution system</li>
<li>the <strong>GPars</strong> parallel library for Groovy</li>
</ul>
<p>And some advanced talk on:</p>
<ul>
<li>how to use Grails for the mobile web</li>
<li>how Grails works under the hood</li>
<li>how we&rsquo;re working on Groovy&rsquo;s core system</li>
<li>how to do advanced meta-programming with Groovy</li>
<li>how to use IntelliJ IDEA to develop with those GR8 technologies</li>
</ul>
<p>There will also be a pretty interesting case study, on how European Patent Office has leveraged the Groovy dynamic language on its platform, showing once again how Groovy is used for serious enterprise work.</p>
<p>As the <a href="http://eu.gr8conf.org/eu2010/agenda/index">agenda</a> there&rsquo;s a lot of very rich content for everybody on all those projects, and all those presentations are done by the <a href="http://eu.gr8conf.org/eu2010/speaker/list">makers, leaders and experts</a> of these projects themselves! To name a few of the <a href="http://eu.gr8conf.org/eu2010/speaker/list">speakers</a>:</p>
<ul>
<li><strong>Jochen Theodorou</strong> - Groovy Tech Lead</li>
<li><strong>Graeme Rocher</strong> - Grails Project Lead</li>
<li><strong>Andres Almiray</strong> - Griffon project lead</li>
<li><strong>Sébastien Blanc</strong> - Developer of a mobile Grails plugin</li>
<li><strong>Hubert K. Ikkink</strong> (Mr. HaKi) - the famous G&amp;G blogger with tons of great tips</li>
<li><strong>Hamlet D&rsquo;Arcy</strong> - Groovy committer</li>
<li><strong>Hans Dokter</strong> - the founder of Gradle</li>
<li><strong>Vaclav Pech</strong> - lead of the GPars project</li>
<li><strong>Philippe Delebarre</strong> - from the European Patent Office</li>
<li><strong>Søren Berg Glasius</strong> - the organizer of the conference</li>
<li><strong>Guillaume Laforge</strong> (yours truely) - Groovy Project Manager</li>
</ul>
<p>With excellent content, star speakers, friendly sponsors, in a nice European location, you really can&rsquo;t afford missing this great event if you&rsquo;re interested in Groovy related technologies!</p>
<p>I&rsquo;m looking forward to meeting you there, so please, <a href="http://eu.gr8conf.org/eu2010/register">register now</a></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Final release of Groovy 1.7!</title><link>https://glaforge.dev/posts/2009/12/22/final-release-of-groovy-1-7/</link><pubDate>Tue, 22 Dec 2009 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2009/12/22/final-release-of-groovy-1-7/</guid><description>&lt;p>The Groovy development team and &lt;a href="http://www.springsource.com/">SpringSource&lt;/a> are very pleased to announce the &lt;strong>final release of Groovy 1.7&lt;/strong>, the most popular and successful dynamic language for the JVM! After two betas and two release candidates, we&amp;rsquo;re are happy to deliver this new and very important milestone to our ever growing user base.&lt;/p>
&lt;p>Over the years, the Groovy project has managed to grow a community, but not only that, a &lt;strong>very rich and active ecosystem&lt;/strong> of Groovy-related projects: the&lt;a href="http://grails.org/">Grails&lt;/a> web stack, the &lt;a href="http://griffon.codehaus.org/">Griffon&lt;/a> swing application framework, the &lt;a href="http://gant.codehaus.org/">Gant&lt;/a> and &lt;a href="http://gradle.org/">Gradle&lt;/a> build solutions, the &lt;a href="http://gaelyk.appspot.com/">Gaelyk&lt;/a> lightweight toolkit for Google App Engine, the &lt;a href="http://gpars.codehaus.org/">Gpars&lt;/a>parallel system, the &lt;a href="http://easyb.org/">Easyb&lt;/a> and &lt;a href="http://www.spockframework.org/">Spock&lt;/a> testing frameworks and the &lt;a href="http://gmock.org/">GMock&lt;/a> mocking library, the &lt;a href="http://codenarc.sourceforge.net/">CodeNarc&lt;/a> and &lt;a href="http://gmetrics.sourceforge.net/">GMetrics&lt;/a> quality tools, and many more! With all these initiatives, the world is even groovier and we&amp;rsquo;re thankful these projects have helped us shape what Groovy is today.&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy development team and <a href="http://www.springsource.com/">SpringSource</a> are very pleased to announce the <strong>final release of Groovy 1.7</strong>, the most popular and successful dynamic language for the JVM! After two betas and two release candidates, we&rsquo;re are happy to deliver this new and very important milestone to our ever growing user base.</p>
<p>Over the years, the Groovy project has managed to grow a community, but not only that, a <strong>very rich and active ecosystem</strong> of Groovy-related projects: the<a href="http://grails.org/">Grails</a> web stack, the <a href="http://griffon.codehaus.org/">Griffon</a> swing application framework, the <a href="http://gant.codehaus.org/">Gant</a> and <a href="http://gradle.org/">Gradle</a> build solutions, the <a href="http://gaelyk.appspot.com/">Gaelyk</a> lightweight toolkit for Google App Engine, the <a href="http://gpars.codehaus.org/">Gpars</a>parallel system, the <a href="http://easyb.org/">Easyb</a> and <a href="http://www.spockframework.org/">Spock</a> testing frameworks and the <a href="http://gmock.org/">GMock</a> mocking library, the <a href="http://codenarc.sourceforge.net/">CodeNarc</a> and <a href="http://gmetrics.sourceforge.net/">GMetrics</a> quality tools, and many more! With all these initiatives, the world is even groovier and we&rsquo;re thankful these projects have helped us shape what Groovy is today.</p>
<p>Groovy 1.7 provides the following new major features and enhancements:</p>
<ul>
<li><strong>Anonymous Inner Classes</strong> and <strong>Nested Classes</strong>, for more Java-friendliness</li>
<li><strong>Annotation enhancements</strong>, with the ability to put annotations on imports, packages and variable declarations)</li>
<li><strong>Grape enhancement</strong> (the Groovy module system)</li>
<li><strong>Power Asserts</strong>, for more readable and expressive assertions</li>
<li>AST (Abstract Syntax Tree) enhancements with the <strong>AST Viewer</strong> and <strong>AST Builder</strong>, for easing the creation of AST transformations</li>
<li>Various dependency upgrades (including the ASM bytecode library to get ready for the JSR-292 InvokeDynamic bytecode instructions)</li>
<li>A <strong>rewrite of the GroovyScriptEngine</strong> for more flexibility and rapidity</li>
<li>Several <strong>GroovyConsole enhancements</strong>, like line numbers, a new output view, and more</li>
<li>Various <strong>SQL improvements</strong>, like batch updates and transaction support</li>
<li>and many more enhancements!</li>
</ul>
<p>You can <strong>learn more about all these features</strong> in the <a href="http://docs.codehaus.org/display/GROOVY/Groovy+1.7+release+notes">Groovy 1.7 release notes</a>.</p>
<p>For further details, you can have a look at our <a href="http://jira.codehaus.org/sr/jira.issueviews:searchrequest-printable/temp/SearchRequest.html?jqlQuery=project+%3D+GROOVY+AND+fixVersion+in+%28%221.7.0%22%2C+%221.7-rc-2%22%2C+%221.7-rc-1%22%2C+%221.7-beta-2%22%2C+%221.7-beta-1%22%29+AND+status+in+%28Resolved%2C+Closed%29+ORDER+BY+priority+DESC&amp;tempMax=1000">JIRA release notes</a> for some more details.</p>
<p>You can download Groovy in the <a href="http://groovy.codehaus.org/Download">download section</a> of our website.</p>
<p>The Groovy development team would particularly thank all the users, contributors and committers who helped us all along to improve the quality and usefulness of the language and its APIs, as well as all those projects in the Groovy ecosystem who influenced us and inspired us.</p>
<p>We&rsquo;re offering our community this new release as a Christmas present, hoping you&rsquo;ll enjoy it as much as we did when working on it, and <strong>we wish you all a very Groovy Christmas</strong>, and all <strong>the best for the coming year</strong>!<br />
Enjoy this new release, and stay tuned for all the upcoming improvements we&rsquo;ll bring you in the future: a more modular and even faster Groovy, additional abilities for more readable and expressive DSLs, and many more exciting features!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Release train: Grails 1.1.2, Groovy 1.6.6 and 1.7-RC-1, as well as Gaelyk 0.3.2</title><link>https://glaforge.dev/posts/2009/11/28/release-train-grails-1-1-2-groovy-1-6-6-and-1-7-rc-1-as-well-as-gaelyk-0-3-2/</link><pubDate>Sat, 28 Nov 2009 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2009/11/28/release-train-grails-1-1-2-groovy-1-6-6-and-1-7-rc-1-as-well-as-gaelyk-0-3-2/</guid><description>&lt;p>Big release days!&lt;/p>
&lt;ul>
&lt;li>Yesterday, &lt;a href="http://www.grails.org/1.1.2+Release+Notes">Grails 1.1.2&lt;/a> was pulled from the oven, while waiting for Spring 3 to be available to release 1.2.&lt;/li>
&lt;li>Today, we &lt;a href="http://docs.codehaus.org/display/GROOVY/2009/11/27/Groovy+1.6.6+and+1.7-RC-1+released">released Groovy 1.6.6 and 1.7-RC-1&lt;/a>, bringing use closer to the final 1.7 release, which would take place before Christmas.&lt;/li>
&lt;li>And tonight, I&amp;rsquo;ve also updated &lt;a href="http://groups.google.com/group/gaelyk/browse_thread/thread/fe09f18440e3812d">Gaelyk 0.3.2&lt;/a>, the lightweight Groovy toolkit for developing applications on Google App Engine. In particular, have a look at the URL routing support and the few bug fixes and also at my &lt;a href="http://glaforge.free.fr/weblog/index.php?itemid=286">presentation at the Devoxx conference&lt;/a>.&lt;/li>
&lt;/ul>
&lt;p>Have fun and enjoy those releases!&lt;/p></description><content:encoded>
<![CDATA[<p>Big release days!</p>
<ul>
<li>Yesterday, <a href="http://www.grails.org/1.1.2+Release+Notes">Grails 1.1.2</a> was pulled from the oven, while waiting for Spring 3 to be available to release 1.2.</li>
<li>Today, we <a href="http://docs.codehaus.org/display/GROOVY/2009/11/27/Groovy+1.6.6+and+1.7-RC-1+released">released Groovy 1.6.6 and 1.7-RC-1</a>, bringing use closer to the final 1.7 release, which would take place before Christmas.</li>
<li>And tonight, I&rsquo;ve also updated <a href="http://groups.google.com/group/gaelyk/browse_thread/thread/fe09f18440e3812d">Gaelyk 0.3.2</a>, the lightweight Groovy toolkit for developing applications on Google App Engine. In particular, have a look at the URL routing support and the few bug fixes and also at my <a href="http://glaforge.free.fr/weblog/index.php?itemid=286">presentation at the Devoxx conference</a>.</li>
</ul>
<p>Have fun and enjoy those releases!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Devoxx presentation on Google App Engine, Groovy and Gaelyk</title><link>https://glaforge.dev/talks/2009/11/20/devoxx-presentation-on-google-app-engine-groovy-and-gaelyk/</link><pubDate>Fri, 20 Nov 2009 00:00:00 +0100</pubDate><guid>https://glaforge.dev/talks/2009/11/20/devoxx-presentation-on-google-app-engine-groovy-and-gaelyk/</guid><description>&lt;p>I&amp;rsquo;m back from Devoxx, where I had a very pleasant stay, nice discussions with many attendees and speakers, and it&amp;rsquo;s always a great opportunity to meet friends you haven&amp;rsquo;t seen in a while! And this time, I was there with my friend Patrick Chanezon from Google, to speak about &lt;a href="http://code.google.com/intl/fr/appengine/">Google App Engine&lt;/a>, &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> and &lt;a href="http://gaelyk.appspot.com/">Gaelyk&lt;/a>. I&amp;rsquo;ve uploaded our slides on Slideshare, and you can view them embedded here on this blog:&lt;/p>
&lt;p>&lt;a href="http://www.slideshare.net/glaforge/google-app-engine-java-groovy-baby">Google App Engine Java, Groovy and Gaelyk&lt;/a>&lt;/p></description><content:encoded>
<![CDATA[<p>I&rsquo;m back from Devoxx, where I had a very pleasant stay, nice discussions with many attendees and speakers, and it&rsquo;s always a great opportunity to meet friends you haven&rsquo;t seen in a while! And this time, I was there with my friend Patrick Chanezon from Google, to speak about <a href="http://code.google.com/intl/fr/appengine/">Google App Engine</a>, <a href="http://groovy.codehaus.org/">Groovy</a> and <a href="http://gaelyk.appspot.com/">Gaelyk</a>. I&rsquo;ve uploaded our slides on Slideshare, and you can view them embedded here on this blog:</p>
<p><a href="http://www.slideshare.net/glaforge/google-app-engine-java-groovy-baby">Google App Engine Java, Groovy and Gaelyk</a></p>
<p>A bit before the presentation, I also fixed a little but annoying bug with the XMPP support in Gaelyk, bumping the version number to 0.3.1, so if you&rsquo;re using 0.3 already, and need incoming XMPP messaging support, please <a href="http://gaelyk.appspot.com/download/">upgrade to 0.3.1</a>.</p>
<p>The presentation at Devoxx seems to have been pretty well received and attended (I think we reached about 800 geeks in the audience, my new personal record) and people seemed to have appreciated the talk and content, as well as my final <a href="http://groovydemos.appspot.com/">Gaelyk demo</a>, interacting with the app through XMPP. This was quite a lot of fun, and just a few hours of work at night to get the demo ready.</p>
<p><strong>Update:</strong> I&rsquo;ve pushed a <a href="http://glaforge.free.fr/groovy/gaelyk-devoxx-demo.zip">zip file of the demo</a> I&rsquo;ve run online, in case you wanna have a look at that code sample.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Gaelyk 0.3 released -- a lightweight Groovy toolkit for Google App Engine</title><link>https://glaforge.dev/posts/2009/11/08/gaelyk-0-3-released-a-lightweight-groovy-toolkit-for-google-app-engine/</link><pubDate>Sun, 08 Nov 2009 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2009/11/08/gaelyk-0-3-released-a-lightweight-groovy-toolkit-for-google-app-engine/</guid><description>&lt;p>Following the &lt;em>conference-driven development&lt;/em> principle, right in time for the &lt;a href="http://www.devoxx.com/">Devoxx&lt;/a> conference and my &lt;a href="http://www.devoxx.com/display/DV09/Google+Appengine+Java+-+Groovy+baby">session&lt;/a> with my friend &lt;a href="http://www.devoxx.com/display/DV09/Patrick+Chanezon">Patrick Chanezon&lt;/a> on Google App Engine Java and Gaelyk/Groovy, I&amp;rsquo;ve just released a new version (0.3) of the &lt;a href="http://gaelyk.appspot.com/">Gaelyk&lt;/a> lightweight Groovy toolkit for Google App Engine.&lt;/p>
&lt;p>This new version fixes a bug, adds some new capabilities, and bring a small change:&lt;/p>
&lt;ul>
&lt;li>The Google services bound to the Groovlets and templates through the binding have been renamed (except userService) to remove the service suffix&lt;/li>
&lt;li>There are some new methods for working with the memcache service, so you can use the map notation (subscript) to access elements of the cache, as well as using the &amp;lsquo;in&amp;rsquo; keyword to check whether a key is present in the cache.&lt;/li>
&lt;li>Since GAE SDK 1.2.6, incoming email support has been added, so Gaelyk 0.3 also adds support for incoming emails.&lt;/li>
&lt;li>There was an issue since the birth of Gaelyk with sending emails, it has now been fixed.&lt;/li>
&lt;/ul>
&lt;p>Please make sure to check the &lt;a href="http://gaelyk.appspot.com/tutorial/">tutorial&lt;/a>, as it&amp;rsquo;s been updated with new sections on these changes and new features.&lt;/p></description><content:encoded>
<![CDATA[<p>Following the <em>conference-driven development</em> principle, right in time for the <a href="http://www.devoxx.com/">Devoxx</a> conference and my <a href="http://www.devoxx.com/display/DV09/Google+Appengine+Java+-+Groovy+baby">session</a> with my friend <a href="http://www.devoxx.com/display/DV09/Patrick+Chanezon">Patrick Chanezon</a> on Google App Engine Java and Gaelyk/Groovy, I&rsquo;ve just released a new version (0.3) of the <a href="http://gaelyk.appspot.com/">Gaelyk</a> lightweight Groovy toolkit for Google App Engine.</p>
<p>This new version fixes a bug, adds some new capabilities, and bring a small change:</p>
<ul>
<li>The Google services bound to the Groovlets and templates through the binding have been renamed (except userService) to remove the service suffix</li>
<li>There are some new methods for working with the memcache service, so you can use the map notation (subscript) to access elements of the cache, as well as using the &lsquo;in&rsquo; keyword to check whether a key is present in the cache.</li>
<li>Since GAE SDK 1.2.6, incoming email support has been added, so Gaelyk 0.3 also adds support for incoming emails.</li>
<li>There was an issue since the birth of Gaelyk with sending emails, it has now been fixed.</li>
</ul>
<p>Please make sure to check the <a href="http://gaelyk.appspot.com/tutorial/">tutorial</a>, as it&rsquo;s been updated with new sections on these changes and new features.</p>
<p>You can download the latest <a href="http://gaelyk.appspot.com/download/">JAR</a> and the latest <a href="http://gaelyk.appspot.com/download/">template project</a> directly from GitHub.</p>
<p>The <a href="http://gaelyk.appspot.com/">Gaelyk website</a> uses that new version of Gaelyk, as well as the latest 1.2.6 SDK for Google App Engine &ndash; The <a href="http://groovyconsole.appspot.com/">Groovy Web Console</a> has not yet to been updated.</p>
<p>Please let me also thank some of the contributors to this release, such as Sean Gilligan, Kazuchika Sekiya, Jinto, for their help with improving the tutorial, and to all those who contributed on the mailing-list or elsewhere. For instance, well done to the Averone company for migrating its <a href="http://www.averone.com.br/en/empresa.html">website</a> to Gaelyk, or the the<a href="http://phone4water.appspot.com/">Phone4Water</a> website also on Gaelyk!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Gaelyk 0.2 released -- a lightweight toolkit for Google App Engine</title><link>https://glaforge.dev/posts/2009/09/15/gaelyk-0-2-released-a-lightweight-toolkit-for-google-app-engine/</link><pubDate>Tue, 15 Sep 2009 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2009/09/15/gaelyk-0-2-released-a-lightweight-toolkit-for-google-app-engine/</guid><description>&lt;p>&lt;a href="http://gaelyk.appspot.com/">Gaelyk&lt;/a> is a lightweight toolkit for developing and deploying applications on &lt;a href="http://code.google.com/appengine/">Google App Engine&lt;/a>.&lt;/p>
&lt;p>As Google recently released an &lt;a href="http://googleappengine.blogspot.com/2009/09/app-engine-sdk-125-released-for-python.html">updated version of their Google App Engine SDK&lt;/a>, providing support for &lt;a href="http://code.google.com/appengine/docs/java/xmpp/overview.html">XMPP/Jabber messaging&lt;/a> and &lt;a href="http://code.google.com/appengine/docs/java/taskqueue/overview.html">Task Queues&lt;/a>, I&amp;rsquo;ve worked on a new Gaelyk version providing support for these new features, with a Groovy touch.&lt;/p>
&lt;p>Gaelyk can be downloaded here: &lt;a href="http://gaelyk.appspot.com/download/">http://gaelyk.appspot.com/download/&lt;/a>&lt;/p>
&lt;p>You can have a look at the latest &lt;a href="http://gaelyk.appspot.com/tutorial/">tutorial&lt;/a> updated with coverage of:&lt;/p>
&lt;ul>
&lt;li>sending / receiving XMPP/Jabber messages,&lt;/li>
&lt;li>using Task Queues,&lt;/li>
&lt;li>also an example of how to do queries on the datastore has been added. Read the tutorial here: &lt;a href="http://gaelyk.appspot.com/tutorial/">http://gaelyk.appspot.com/tutorial/&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>The &lt;a href="http://gaelyk.appspot.com/">Gaelyk website&lt;/a> uses that new version of Gaelyk, as well as the latest 1.2.5 SDK for Google App Engine. The &lt;a href="http://groovyconsole.appspot.com/">Groovy Web Console&lt;/a> will also be updated shortly to use that new version.&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://gaelyk.appspot.com/">Gaelyk</a> is a lightweight toolkit for developing and deploying applications on <a href="http://code.google.com/appengine/">Google App Engine</a>.</p>
<p>As Google recently released an <a href="http://googleappengine.blogspot.com/2009/09/app-engine-sdk-125-released-for-python.html">updated version of their Google App Engine SDK</a>, providing support for <a href="http://code.google.com/appengine/docs/java/xmpp/overview.html">XMPP/Jabber messaging</a> and <a href="http://code.google.com/appengine/docs/java/taskqueue/overview.html">Task Queues</a>, I&rsquo;ve worked on a new Gaelyk version providing support for these new features, with a Groovy touch.</p>
<p>Gaelyk can be downloaded here: <a href="http://gaelyk.appspot.com/download/">http://gaelyk.appspot.com/download/</a></p>
<p>You can have a look at the latest <a href="http://gaelyk.appspot.com/tutorial/">tutorial</a> updated with coverage of:</p>
<ul>
<li>sending / receiving XMPP/Jabber messages,</li>
<li>using Task Queues,</li>
<li>also an example of how to do queries on the datastore has been added. Read the tutorial here: <a href="http://gaelyk.appspot.com/tutorial/">http://gaelyk.appspot.com/tutorial/</a></li>
</ul>
<p>The <a href="http://gaelyk.appspot.com/">Gaelyk website</a> uses that new version of Gaelyk, as well as the latest 1.2.5 SDK for Google App Engine. The <a href="http://groovyconsole.appspot.com/">Groovy Web Console</a> will also be updated shortly to use that new version.</p>
<p>Have fun with Gaelyk!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Griffon, the holy grail of Swing, is one year old</title><link>https://glaforge.dev/posts/2009/09/10/griffon-the-holy-grail-of-swing-is-one-year-old/</link><pubDate>Thu, 10 Sep 2009 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2009/09/10/griffon-the-holy-grail-of-swing-is-one-year-old/</guid><description>&lt;p>Time flies, soooo fast! &lt;a href="http://griffon.codehaus.org/">Griffon&lt;/a>, the Groovy MVC framework for building desktop applications, is already one year old. Developers know &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> and &lt;a href="http://grails.org/">Grails&lt;/a> very well, but there are many other Groovy-based tools and frameworks, and Griffon is a very nice and successful animal of the Groovy ecosystem.&lt;/p>
&lt;p>Others are already celebrating this first anniversary on &lt;a href="http://search.twitter.com/search?q=griffon">twitter&lt;/a>, as well as in the blogosphere, like some of the Griffon developers:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://jshingler.blogspot.com/2009/09/griffon-celebrates.html">Jim Shingler&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://jameswilliams.be/blog/entry/155">James Williams&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.jroller.com/aalmiray/entry/happy_birthday_griffon">Andres Almiray&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://josh-in-antarctica.blogspot.com/2009/09/happy-birthday-griffon.html">Josh Reed&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://shemnon.com/speling/2009/09/taking-flight-a-year-of-griffo-1.html">Danno Ferrin&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>With tons of plugins, books in the writing, Griffon is really gaining a lot of momentum and mindshare!&lt;/p></description><content:encoded>
<![CDATA[<p>Time flies, soooo fast! <a href="http://griffon.codehaus.org/">Griffon</a>, the Groovy MVC framework for building desktop applications, is already one year old. Developers know <a href="http://groovy.codehaus.org/">Groovy</a> and <a href="http://grails.org/">Grails</a> very well, but there are many other Groovy-based tools and frameworks, and Griffon is a very nice and successful animal of the Groovy ecosystem.</p>
<p>Others are already celebrating this first anniversary on <a href="http://search.twitter.com/search?q=griffon">twitter</a>, as well as in the blogosphere, like some of the Griffon developers:</p>
<ul>
<li><a href="http://jshingler.blogspot.com/2009/09/griffon-celebrates.html">Jim Shingler</a></li>
<li><a href="http://jameswilliams.be/blog/entry/155">James Williams</a></li>
<li><a href="http://www.jroller.com/aalmiray/entry/happy_birthday_griffon">Andres Almiray</a></li>
<li><a href="http://josh-in-antarctica.blogspot.com/2009/09/happy-birthday-griffon.html">Josh Reed</a></li>
<li><a href="http://shemnon.com/speling/2009/09/taking-flight-a-year-of-griffo-1.html">Danno Ferrin</a></li>
</ul>
<p>With tons of plugins, books in the writing, Griffon is really gaining a lot of momentum and mindshare!</p>
<p>Happy Birtday! Long live the Griffon!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Le podcast des Cast Codeurs est sorti !</title><link>https://glaforge.dev/posts/2009/04/14/le-podcast-des-cast-codeurs-est-sorti/</link><pubDate>Tue, 14 Apr 2009 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2009/04/14/le-podcast-des-cast-codeurs-est-sorti/</guid><description>&lt;p>Une fois n&amp;rsquo;est pas coutume, je bloguerai en français dans cette catégorie tech, pour vous annoncer la sortie du podcast des &lt;a href="http://lescastcodeurs.com/">Cast Codeurs&lt;/a> !&lt;/p>
&lt;p>Les Cast Codeurs, c&amp;rsquo;est un podcast en français dans le code sur Java par Emmanuel Bernard (JBoss / Hibernate), Guillaume Laforge (SpringSource / Groovy), Antonio Goncalves (freelance / Paris JUG lead) et Vincent Massol (XWiki / Maven).&lt;/p>
&lt;p>Restez informés sur les sujets brûlants de l&amp;rsquo;industrie Java. Plongez sur un sujet précis avec l&amp;rsquo;interview de l&amp;rsquo;éposiode. Supportez les radotages de vos hôtes.&lt;/p></description><content:encoded>
<![CDATA[<p>Une fois n&rsquo;est pas coutume, je bloguerai en français dans cette catégorie tech, pour vous annoncer la sortie du podcast des <a href="http://lescastcodeurs.com/">Cast Codeurs</a> !</p>
<p>Les Cast Codeurs, c&rsquo;est un podcast en français dans le code sur Java par Emmanuel Bernard (JBoss / Hibernate), Guillaume Laforge (SpringSource / Groovy), Antonio Goncalves (freelance / Paris JUG lead) et Vincent Massol (XWiki / Maven).</p>
<p>Restez informés sur les sujets brûlants de l&rsquo;industrie Java. Plongez sur un sujet précis avec l&rsquo;interview de l&rsquo;éposiode. Supportez les radotages de vos hôtes.</p>
<p>Ecoutez-nous et faites passer le message au tour de vous ! Le JavaPosse n&rsquo;a qu&rsquo;à bien se tenir (ou alors faut qu&rsquo;ils se mettent à parler français !!!)</p>
<ul>
<li>Web : <a href="http://lescastcodeurs.com/">http://lescastcodeurs.com/</a></li>
<li>iTunes : <a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=312239675">http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=312239675</a></li>
<li>Podcast syndication : <a href="http://lescastcodeurs.com/podcast-rss">http://lescastcodeurs.com/podcast-rss</a></li>
<li>Blog feed : <a href="http://lescastcodeurs.com/feed/">http://lescastcodeurs.com/feed/</a></li>
<li>Vos commentaires : <a href="http://glaforge.free.fr/weblog/commentaire@lescastcodeurs.com">commentaire@lescastcodeurs.com</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Write Groovy applications on Google App Engine!</title><link>https://glaforge.dev/posts/2009/04/08/write-groovy-applications-on-google-app-engine/</link><pubDate>Wed, 08 Apr 2009 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2009/04/08/write-groovy-applications-on-google-app-engine/</guid><description>&lt;p>The news has already spread all around, even on our mailing-lists, but let me echo it again here:&lt;/p>
&lt;p>&lt;strong>Groovy is now supported in the newly released Google App Engine Java platform!&lt;/strong>&lt;/p>
&lt;p>My friend Didier Girard beats me to it and had already &lt;a href="http://www.insideit.fr/post/2009/04/08/Java-et-Groovy-sont-maintenant-supportes-par-Google-App-Engine">blogged about the support of both Java and Groovy&lt;/a> (in French). He was quicker than me&amp;hellip; or at least woke up earlier than me ;-)&lt;/p>
&lt;p>SpringSource&amp;rsquo;s worked with Google to ensure that Groovy would run well on their platform. Big thanks to the work of Jochen Theodorou, &lt;a href="http://weblog.dangertree.net/">Matt Taylor&lt;/a> and myself, for making this possible!&lt;/p></description><content:encoded>
<![CDATA[<p>The news has already spread all around, even on our mailing-lists, but let me echo it again here:</p>
<p><strong>Groovy is now supported in the newly released Google App Engine Java platform!</strong></p>
<p>My friend Didier Girard beats me to it and had already <a href="http://www.insideit.fr/post/2009/04/08/Java-et-Groovy-sont-maintenant-supportes-par-Google-App-Engine">blogged about the support of both Java and Groovy</a> (in French). He was quicker than me&hellip; or at least woke up earlier than me ;-)</p>
<p>SpringSource&rsquo;s worked with Google to ensure that Groovy would run well on their platform. Big thanks to the work of Jochen Theodorou, <a href="http://weblog.dangertree.net/">Matt Taylor</a> and myself, for making this possible!</p>
<p>We <a href="http://docs.codehaus.org/display/GROOVY/2009/04/07/Groovy+1.5.8+and+1.6.1+are+out">released Groovy 1.6.1</a>, with key fixes, for that purpose, yesterday, in time for the big news!</p>
<p>If you want to have a go at it, be quick, as they will only allow 10000 developers to access AppEngine Java initially.</p>
<p>I&rsquo;ve written a <a href="http://blog.springsource.com/2009/04/07/write-your-google-app-engine-applications-in-groovy/">tutorial for your first steps for Groovy on AppEngine</a>.</p>
<p>I&rsquo;ve also written a small <a href="http://myowngroovy.appspot.com/demo.groovy">Google Maps / Geocoding service / Flickr demo in 90 lines of Groovy code</a>.</p>
<p>Enjoy!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Announcing the GR8 Conference: a conference dedicated to Groovy, Grails and Griffon</title><link>https://glaforge.dev/posts/2009/03/04/announcing-the-gr8-conference-a-conference-dedicated-to-groovy-grails-and-griffon/</link><pubDate>Wed, 04 Mar 2009 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2009/03/04/announcing-the-gr8-conference-a-conference-dedicated-to-groovy-grails-and-griffon/</guid><description>&lt;p>I&amp;rsquo;m pleased to announce here the organization of a European conference dedicated to Groovy, Grails and Griffon:GR8 Conference — Copenhagen — May 2009&lt;/p>
&lt;p>The &lt;strong>GR8 Conference&lt;/strong> is an affordable two-day conference taking place in &lt;a href="http://www.gr8conf.org/location.gsp">Copenhagen, Denmark&lt;/a>, on &lt;a href="http://www.gr8conf.org/agenda.gsp">May 18th and 19th 2009&lt;/a>, organized by &lt;a href="http://www.javagruppen.dk/">Javagruppen&lt;/a>(Danish JUG) and &lt;a href="http://www.springsource.com/g2one">SpringSource&lt;/a>, dedicated to the &lt;strong>Groovy&lt;/strong> dynamic language, the &lt;strong>Grails&lt;/strong> web framework, the &lt;strong>Griffon&lt;/strong> Swing framework, and other great technologies — hence the pun and code name GR8.&lt;/p></description><content:encoded>
<![CDATA[<p>I&rsquo;m pleased to announce here the organization of a European conference dedicated to Groovy, Grails and Griffon:GR8 Conference — Copenhagen — May 2009</p>
<p>The <strong>GR8 Conference</strong> is an affordable two-day conference taking place in <a href="http://www.gr8conf.org/location.gsp">Copenhagen, Denmark</a>, on <a href="http://www.gr8conf.org/agenda.gsp">May 18th and 19th 2009</a>, organized by <a href="http://www.javagruppen.dk/">Javagruppen</a>(Danish JUG) and <a href="http://www.springsource.com/g2one">SpringSource</a>, dedicated to the <strong>Groovy</strong> dynamic language, the <strong>Grails</strong> web framework, the <strong>Griffon</strong> Swing framework, and other great technologies — hence the pun and code name GR8.</p>
<ul>
<li>Groovy dynamic language</li>
<li>Grails agile web application framework</li>
<li>Griffon Swing framework</li>
</ul>
<h2 id="speakers-and-audience">Speakers and audience</h2>
<p>All the sessions and labs of the <a href="http://www.gr8conf.org/agenda.gsp">agenda</a> will be presented by <a href="http://www.gr8conf.org/speakers.gsp">experts in the field</a> and makers of those technologies, and will give you — developers, tech leads and architects — a practical and <strong>hands-on experience</strong> on all topics covered.</p>
<h2 id="registration">Registration</h2>
<p>If you <a href="http://www.gr8conf.org/registration/index">register</a> before April 1st, you&rsquo;ll benefit from our <strong>Early Bird rate</strong>, whereas after that date, the regular price will apply. So be sure to register in advance, both for the <strong>affordable price</strong> and because the <strong>seats are limited</strong>!</p>
<h2 id="our-mission-statement">Our mission statement</h2>
<p>Our goals organizing the conference are the following:</p>
<ul>
<li>Spread the word on those great technologies</li>
<li>Help you learn how to best leverage and <strong>integrate those technologies</strong></li>
<li>Get <strong>up-to-speed quickly</strong> for developing your next project</li>
<li>Make you come back to work <strong>proficient and productive</strong> with Groovy, Grails and Griffon</li>
</ul>
<h2 id="why-you-should-attend">Why you should attend</h2>
<p>Here are several reasons why you should attend this conference, beyond just learning more about Groovy, Grails and Griffon:</p>
<ul>
<li>Learn directly <strong>from the experts</strong></li>
<li><strong>Get up-to-speed rapidly</strong> on Groovy, Grails and Griffon</li>
<li>Meet other enthusiasts and share your experiences with them</li>
<li>Foster a community around those technologies</li>
<li><strong>Sharpen your skills</strong> in a down economy to get ready when the tables are turning back</li>
</ul>
<h2 id="the-benefits">The benefits</h2>
<p>Beyond learning more about Groovy, Grails and Griffon, all attendees will bring back home:</p>
<ul>
<li>a 90-day evaluation <strong>license for IntelliJ IDEA</strong>, the best IDE for developing with Groovy, Grails and Griffon</li>
<li>a chance to win a free <a href="http://www.springsource.com/training">SpringSource training</a> on Groovy &amp; Grails, and a <strong>discount</strong> if you decide to follow one of those training courses</li>
<li><strong>coupons</strong> for discounts on major Groovy and Grails books</li>
<li>a special edition of <a href="http://www.groovymag.com/">GroovyMag</a>, the Groovy/Grails/Griffon developer magazine</li>
<li>all <strong>code samples</strong> of the conference on a <strong>USB key</strong></li>
<li>a brochure on <strong>deploying Grails applications in the cloud</strong>, on the <a href="http://www.mor.ph/grails?utm_source=gr8&amp;utm_medium=cpc&amp;utm_campaign=gr8">Morph AppSpace</a> cloud computing platform</li>
<li>access to all the <strong>video recordings of the sessions</strong> on <a href="http://www.parleys.com/">Parleys.com</a> after the conference</li>
</ul>
<p>We&rsquo;re looking forward to meeting you at the conference.</p>
<p>The GR8 Conference organizers.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Lots of Groovy related news!</title><link>https://glaforge.dev/posts/2009/02/27/lots-of-groovy-related-news/</link><pubDate>Fri, 27 Feb 2009 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2009/02/27/lots-of-groovy-related-news/</guid><description>&lt;p>There&amp;rsquo;s always a lot of activity around Groovy and Groovy-related technologies, but these days, it&amp;rsquo;s pretty hot!&lt;/p>
&lt;p>First of all, InfoQ released my &amp;ldquo;&lt;a href="http://www.infoq.com/articles/groovy-1-6">What&amp;rsquo;s new in Groovy 1.6?&lt;/a>&amp;rdquo; article going in depth into all the new features of Groovy 1.6, with explanations and code samples. Please &lt;a href="http://www.dzone.com/links/whats_new_in_groovy_16_detailed_article_on_infoq.html">vote for it on DZone&lt;/a> (Guerilla Marketing rulezzz)&lt;/p>
&lt;p>If you want to attend a &lt;a href="http://www.gr8conf.org/">Groovy / Grails / Griffon dedicated conference&lt;/a>, you should definitely go to the GR8 Conference, in Copenhagen, Denmark, in May. It&amp;rsquo;s going to rock, with star speakers, hands-on sessions, several interesting benefits in the attendee package, and more. Check it out!&lt;/p></description><content:encoded>
<![CDATA[<p>There&rsquo;s always a lot of activity around Groovy and Groovy-related technologies, but these days, it&rsquo;s pretty hot!</p>
<p>First of all, InfoQ released my &ldquo;<a href="http://www.infoq.com/articles/groovy-1-6">What&rsquo;s new in Groovy 1.6?</a>&rdquo; article going in depth into all the new features of Groovy 1.6, with explanations and code samples. Please <a href="http://www.dzone.com/links/whats_new_in_groovy_16_detailed_article_on_infoq.html">vote for it on DZone</a> (Guerilla Marketing rulezzz)</p>
<p>If you want to attend a <a href="http://www.gr8conf.org/">Groovy / Grails / Griffon dedicated conference</a>, you should definitely go to the GR8 Conference, in Copenhagen, Denmark, in May. It&rsquo;s going to rock, with star speakers, hands-on sessions, several interesting benefits in the attendee package, and more. Check it out!</p>
<p>And to finish, there are tons of releases:</p>
<ul>
<li><a href="http://glaforge.free.fr/weblog/index.php?itemid=263">Groovy 1.6</a></li>
<li><a href="http://docs.codehaus.org/display/GROOVY/2009/02/27/GMaven+1.0-rc-5+released">GMaven 1.0-rc-5: Groovy Maven plugin</a></li>
<li><a href="http://groovy.dzone.com/announcements/griffon-01-rc2-builds">Griffon 0.1-rc2</a></li>
<li><a href="http://grails.org/news/14369">Grails 1.1 RC2</a></li>
</ul>
<p>And if you&rsquo;ve got some artistic talent or if you know some talented designers, the <a href="http://griffon.codehaus.org/Logo+Contest">Griffon team is still looking for a logo</a>!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>What's new in Groovy 1.6</title><link>https://glaforge.dev/posts/2009/02/27/whats-new-in-groovy-16/</link><pubDate>Fri, 27 Feb 2009 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2009/02/27/whats-new-in-groovy-16/</guid><description>&lt;p>&lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> is a very successful and powerful &lt;strong>dynamic language for the Java Virtual Machine&lt;/strong> that provides &lt;strong>seamless integration with Java&lt;/strong>, and has its roots firmly planted in Java itself for the syntax and APIs and other languages such as Smalltalk, Python or Ruby for its dynamic capabilities.&lt;/p>
&lt;p>Groovy is used in many Open Source projects such as &lt;a href="http://grails.org/">Grails&lt;/a>, &lt;a href="http://www.springframework.org/">Spring&lt;/a>, JBoss Seam and more, as well as &lt;strong>integrated in commercial products&lt;/strong> and &lt;strong>Fortune 500 mission-critical applications&lt;/strong> for its scripting capabilities offering a nice extension mechanism to these applications, or for its ability to let subject matter experts and developers author embedded Domain-Specific Languages to &lt;strong>express business concepts in a readable and maintainable fashion&lt;/strong>.&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://groovy.codehaus.org/">Groovy</a> is a very successful and powerful <strong>dynamic language for the Java Virtual Machine</strong> that provides <strong>seamless integration with Java</strong>, and has its roots firmly planted in Java itself for the syntax and APIs and other languages such as Smalltalk, Python or Ruby for its dynamic capabilities.</p>
<p>Groovy is used in many Open Source projects such as <a href="http://grails.org/">Grails</a>, <a href="http://www.springframework.org/">Spring</a>, JBoss Seam and more, as well as <strong>integrated in commercial products</strong> and <strong>Fortune 500 mission-critical applications</strong> for its scripting capabilities offering a nice extension mechanism to these applications, or for its ability to let subject matter experts and developers author embedded Domain-Specific Languages to <strong>express business concepts in a readable and maintainable fashion</strong>.</p>
<p>In this article, <a href="http://www.springsource.com/people/glaforge">Guillaume Laforge</a>, Groovy Project Manager and Head of Groovy Development at <a href="http://www.springsource.com/">SpringSource</a>, will go through an overview of the novelties offered by the newly released Groovy 1.6.</p>
<h2 id="overview-of-groovy-16">Overview of Groovy 1.6</h2>
<p>As we shall see in this article, the main highlights of this Groovy 1.6 release are:</p>
<ul>
<li><strong>Greater compile-time and runtime performance improvements</strong></li>
<li>Multiple assignments</li>
<li>Optional return in <code>if</code>/<code>else</code> and <code>try</code>/<code>catch</code> blocks</li>
<li>Java 5 annotation definition</li>
<li><strong>AST transformations</strong> and all the provided transformation annotations like <code>@Singleton</code>, <code>@Lazy</code>, <code>@Immutable</code>, <code>@Delegate</code> and friends</li>
<li>The Grape module and dependency system and its <code>@Grab</code> transformation</li>
<li>Various Swing builder improvements, thanks to the Swing / <a href="http://griffon.codehaus.org/">Griffon</a> team, as well as several Swing console improvements</li>
<li>The integration of <strong>JMX builder</strong></li>
<li>Various <strong>metaprogramming improvements</strong>, like the EMC DSL, per-instance metaclasses even for POJOs, and runtime mixins</li>
<li><strong>JSR-223</strong> scripting engine built-in</li>
<li>Out-of-the-box <strong>OSGi readiness</strong></li>
</ul>
<p>All those improvements and new features serve one goal: <strong>helping developers be more productive and more agile</strong>, by:</p>
<ul>
<li>Focusing more on the task at hand than on boiler-plate technical code</li>
<li>Leveraging existing Enterprise APIs rather than reinventing the wheel</li>
<li>Improving the overal performance and quality of the language</li>
<li>Enabling developers to customize the language at will to derive their own Domain-Specific Languages</li>
</ul>
<p>But beyond all these important aspects, <strong>Groovy is not just a language, it&rsquo;s a whole ecosystem</strong>.</p>
<p>The improvements in Groovy&rsquo;s generated bytecode information helps capable tools coverage like <a href="http://docs.codehaus.org/display/GROOVY/Test+Coverage">Cobertura</a> and its Groovy support, or pave the way for new utilities like <a href="http://codenarc.sourceforge.net/">CodeNarc</a> for static code analysis for Groovy.</p>
<p>The malleability of the syntax of the language and its metaprogramming capabilities give birth to advanced testing tools such as the <a href="http://easyb.org/">Easyb</a> Behavior-Driven-Development project, the <a href="http://code.google.com/p/gmock/">GMock</a> mocking library or the <a href="http://code.google.com/p/spock/">Spock</a> testing and specification framework.</p>
<p>Again, Groovy&rsquo;s flexibility and expressivity and its scripting capabilities open the doors to advanced build scripting or infrastructure systems for your continuous integration practices and project build solutions, such as <a href="http://gant.codehaus.org/">Gant</a> and <a href="http://www.gradle.org/">Graddle</a>.</p>
<p>At the tooling level, Groovy also progresses, for instance with its <code>groovydoc</code> Ant task to let you generate proper JavaDoc covering, documenting and interlinking both your Groovy and Java source files for your Groovy/Java mixed projects.</p>
<p>And at the same time, IDE makers improve their support for Groovy, by giving users powerful weapons such as cross-language code refactoring, profound understanding of dynamic language idioms, code completion, and more, to make developers productive when using Groovy in their projects.</p>
<p>Now, armed with this knowledge of the Groovy world, it&rsquo;s time to dive into the novelties of Groovy 1.6!</p>
<h2 id="performance-improvements">Performance improvements</h2>
<p>A lot of care has been taken to improve both the compile-time and runtime performance of Groovy, compared to previous releases.</p>
<p>The <strong>compiler is 3 to 5 times faster</strong> than in previous releases. This improvement has also been backported in the 1.5.x branch, so that both the old maintenance branch and the current stable branch benefit from this work. Thanks to class lookup caches, the bigger the project, the faster the compilation will be.</p>
<p>However, the most noticeable changes will be in the general runtime performance improvements of Groovy. We used several benchmarks from the <a href="http://shootout.alioth.debian.org/">Great Language Shootout</a> to measure our progress. On those we selected, compared to the old Groovy 1.5.x line, the <strong>performance improvements ranged from 150% to 460%</strong>. Micro-benchmarks obviously rarely reflect the kind of code you have in your own projects, but the overal performance of your projects should improve significantly.</p>
<h2 id="multiple-assignments">Multiple assignments</h2>
<p>In Groovy 1.6, there is only one syntax addition for being able to define and assign several variables at once:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> <span style="color:#666">(</span>a<span style="color:#666">,</span> b<span style="color:#666">)</span> <span style="color:#666">=</span> <span style="color:#666">[</span><span style="color:#40a070">1</span><span style="color:#666">,</span> <span style="color:#40a070">2</span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> a <span style="color:#666">==</span> <span style="color:#40a070">1</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> b <span style="color:#666">==</span> <span style="color:#40a070">2</span>
</span></span></code></pre></div><p>A more meaninful example may be methods returning longitute and latitude coordinates. If these coordinates are represented as a list of two elements, you can easily get back to each element as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> <span style="color:#06287e">geocode</span><span style="color:#666">(</span>String location<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// implementation returns [48.824068, 2.531733] for Paris, France
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> <span style="color:#666">(</span>lat<span style="color:#666">,</span> <span style="color:#902000">long</span><span style="color:#666">)</span> <span style="color:#666">=</span> geocode<span style="color:#666">(</span><span style="color:#4070a0">&#34;Paris, France&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> lat <span style="color:#666">==</span> <span style="color:#40a070">48.824068</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#902000">long</span> <span style="color:#666">==</span> <span style="color:#40a070">2.531733</span>
</span></span></code></pre></div><p>And you can also define the types of the variables in one shot as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> <span style="color:#666">(</span><span style="color:#902000">int</span> i<span style="color:#666">,</span> String s<span style="color:#666">)</span> <span style="color:#666">=</span> <span style="color:#666">[</span><span style="color:#40a070">1</span><span style="color:#666">,</span> <span style="color:#4070a0">&#39;Groovy&#39;</span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> i <span style="color:#666">==</span> <span style="color:#40a070">1</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> s <span style="color:#666">==</span> <span style="color:#4070a0">&#39;Groovy&#39;</span>
</span></span></code></pre></div><p>For the assignment (with prior definition of the variables), just omit the <code>def</code> keyword:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> firstname<span style="color:#666">,</span> lastname
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#666">(</span>firstname<span style="color:#666">,</span> lastname<span style="color:#666">)</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Guillaume Laforge&#34;</span><span style="color:#666">.</span><span style="color:#4070a0">tokenize</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> firstname <span style="color:#666">==</span> <span style="color:#4070a0">&#34;Guillaume&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> lastname <span style="color:#666">==</span> <span style="color:#4070a0">&#34;Laforge&#34;</span>
</span></span></code></pre></div><p>If the list on the right-hand side contains more elements than the number of variables on the left-hand side, only the first elements will be assigned in order into the variables. Also, when there are less elements than variables, the extra variables will be assigned null.</p>
<p>So for the case with more variables than list elements, here, <code>c</code> will be <code>null</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> elements <span style="color:#666">=</span> <span style="color:#666">[</span><span style="color:#40a070">1</span><span style="color:#666">,</span> <span style="color:#40a070">2</span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> <span style="color:#666">(</span>a<span style="color:#666">,</span> b<span style="color:#666">,</span> c<span style="color:#666">)</span> <span style="color:#666">=</span> elements
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> a <span style="color:#666">==</span> <span style="color:#40a070">1</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> b <span style="color:#666">==</span> <span style="color:#40a070">2</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> c <span style="color:#666">==</span> <span style="color:#007020;font-weight:bold">null</span>
</span></span></code></pre></div><p>Whereas in the case where there are more list elements than variables, we&rsquo;ll get the following expectations:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> elements <span style="color:#666">=</span> <span style="color:#666">[</span><span style="color:#40a070">1</span><span style="color:#666">,</span> <span style="color:#40a070">2</span><span style="color:#666">,</span> <span style="color:#40a070">3</span><span style="color:#666">,</span> <span style="color:#40a070">4</span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> <span style="color:#666">(</span>a<span style="color:#666">,</span> b<span style="color:#666">,</span> c<span style="color:#666">)</span> <span style="color:#666">=</span> elements
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> a <span style="color:#666">==</span> <span style="color:#40a070">1</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> b <span style="color:#666">==</span> <span style="color:#40a070">2</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> c <span style="color:#666">==</span> <span style="color:#40a070">3</span>
</span></span></code></pre></div><p>For the curious minds, supporting multiple assignments also means we can do the standard school swap case in one line:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// given those two variables
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> a <span style="color:#666">=</span> <span style="color:#40a070">1</span><span style="color:#666">,</span> b <span style="color:#666">=</span> <span style="color:#40a070">2</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// swap variables with a list
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#666">(</span>a<span style="color:#666">,</span> b<span style="color:#666">)</span> <span style="color:#666">=</span> <span style="color:#666">[</span>b<span style="color:#666">,</span> a<span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> a <span style="color:#666">==</span> <span style="color:#40a070">2</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> b <span style="color:#666">==</span> <span style="color:#40a070">1</span>
</span></span></code></pre></div><h2 id="annotation-definition">Annotation definition</h2>
<p>Actually, when I said that multiple assignments were the sole syntax addition, it&rsquo;s not entirely true. Groovy supported the syntax for annotation definition even in Groovy 1.5, but we had not implemented the feature completely. Fortunately, this is now fixed, and it wraps up all the Java 5 features supported by Groovy, such as <strong>static imports</strong>, <strong>generics</strong>, <strong>annotations</strong>, and <strong>enums</strong>, making Groovy the <strong>sole alternative dynamic language for the JVM supporting all those Java 5 features</strong>, which is critical for a seamless Java integration story, and for the usage in Enterprise frameworks relying on annotations, generics and more, like JPA, EJB3, Spring, TestNG, etc.</p>
<h2 id="optional-return-for-ifelse-and-trycatchfinally-blocks">Optional return for <code>if</code>/<code>else</code> and <code>try</code>/<code>catch</code>/<code>finally</code> blocks</h2>
<p>It is now possible for <code>if</code>/<code>else</code> and <code>try</code>/<code>catch</code>/<code>finally</code> blocks to return a value when they are the last expression in a method or a closure. No need to explicitly use the <code>return</code> keyword inside these constructs, as long as they are the latest expression in the block of code.</p>
<p>As an example, the following method will return <code>1</code>, although the <code>return</code> keyword was omitted.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> <span style="color:#06287e">method</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span><span style="color:#007020;font-weight:bold">true</span><span style="color:#666">)</span> <span style="color:#40a070">1</span> <span style="color:#007020;font-weight:bold">else</span> <span style="color:#40a070">0</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">method</span><span style="color:#666">()</span> <span style="color:#666">==</span> <span style="color:#40a070">1</span>
</span></span></code></pre></div><p>For <code>try</code>/<code>catch</code>/<code>finally</code> blocks, the last expression evaluated is the one being returned. If an exception is thrown in the <code>try</code> block, the last expression in the <code>catch</code> block is returned instead. Note that <code>finally</code> blocks don&rsquo;t return any value.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> <span style="color:#06287e">method</span><span style="color:#666">(</span>bool<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">try</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>bool<span style="color:#666">)</span> <span style="color:#007020;font-weight:bold">throw</span> <span style="color:#007020;font-weight:bold">new</span> Exception<span style="color:#666">(</span><span style="color:#4070a0">&#34;foo&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#40a070">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">catch</span><span style="color:#666">(</span>e<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#40a070">2</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">finally</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#40a070">3</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">method</span><span style="color:#666">(</span><span style="color:#007020;font-weight:bold">false</span><span style="color:#666">)</span> <span style="color:#666">==</span> <span style="color:#40a070">1</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">method</span><span style="color:#666">(</span><span style="color:#007020;font-weight:bold">true</span><span style="color:#666">)</span> <span style="color:#666">==</span> <span style="color:#40a070">2</span>
</span></span></code></pre></div><h2 id="ast-transformations">AST Transformations</h2>
<p>Although at times, it may sound like a good idea to extend the syntax of Groovy to implement new features (like this is the case for instance for multiple assignments), most of the time, we can&rsquo;t just add a new keyword to the grammar, or create some new syntax construct to represent a new concept. However, with the idea of AST (Abstract Syntax Tree) Transformations, we are able to tackle new and innovative ideas without necessary grammar changes.</p>
<p>When the Groovy compiler compiles Groovy scripts and classes, at some point in the process, the source code will end up being represented in memory in the form of a Concrete Syntax Tree, then transformed into an Abstract Syntax Tree. The purpose of AST Transformations is to let developers hook into the compilation process to be able to modify the AST before it is turned into bytecode that will be run by the JVM.</p>
<p><strong>AST Transformations provides Groovy with improved compile-time metaprogramming capabilities</strong> allowing powerful flexibility at the language level, without a runtime performance penalty.</p>
<p>There are two kinds of transformations: global and local transformations.</p>
<ul>
<li>
<p>Global transformations are applied to by the compiler on the code being compiled, wherever the transformation apply. A JAR added to the classpath of the compiler should contain a service locator file at <code>META-INF/services/org.codehaus.groovy.transform.ASTTransformation</code> with a line with the name of the transformation class. The transformation class must have a no-args constructor and implement the <code>org.codehaus.groovy.transform.ASTTransformation</code> interface. It will be run against every source in the compilation, so be sure to not create transformations which scan all the AST in an expansive and time-consuming manner, to keep the compiler fast.</p>
</li>
<li>
<p>Local transformations are transformations applied locally by annotating code elements you want to transform. For this, we reuse the annotation notation, and those annotations should implement <code>org.codehaus.groovy.transform.ASTTransformation</code>. The compiler will discover them and apply the transformation on these code elements.</p>
</li>
</ul>
<p>Groovy 1.6 provides several local transformation annotations, in the Groovy Swing Builder for data binding (<code>@Bindable</code> and <code>@Vetoable</code>), in the Grape module system for adding script library dependencies (<code>@Grab</code>), or as general language features without requiring any syntax change to support them (<code>@Singleton</code>, <code>@Immutable</code>, <code>@Delegate</code>, <code>@Lazy</code>, <code>@Newify</code>, <code>@Category</code>, <code>@Mixin</code> and <code>@PackageScope</code>). Let&rsquo;s have a look at some of these transformations (<code>@Bindable</code> and <code>@Vetoable</code> will be covered in the section related to the Swing enhancements, and <code>@Grab</code> in the section about Grape).</p>
<h3 id="singleton">@Singleton</h3>
<p>Whether the singleton is pattern or an anti-pattern, there are still some cases where we need to create singletons. We&rsquo;re used to create a private constructor, a <code>getInstance()</code> method for a static field or even an initialized <code>public static final</code> field. So instead of writing code like this in Java:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">T</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">final</span><span style="color:#bbb"> </span>T<span style="color:#bbb"> </span>instance<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>T();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#06287e">T</span>()<span style="color:#bbb"> </span>{}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>You just need to annotate your type with the <code>@Singleton</code> annotation:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Singleton</span> <span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">T</span> <span style="color:#666">{}</span>
</span></span></code></pre></div><p>The singleton instance can then simply be accessed with <code>T.instance</code> (direct public field access).</p>
<p>You can also have the lazy loading approach with an additional annotation parameter:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Singleton</span><span style="color:#666">(</span>lazy <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">true</span><span style="color:#666">)</span> <span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">T</span> <span style="color:#666">{}</span>
</span></span></code></pre></div><p>Would become more or less equivalent to this Groovy class:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">T</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">private</span> <span style="color:#007020;font-weight:bold">static</span> <span style="color:#007020;font-weight:bold">volatile</span> T instance
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">private</span> <span style="color:#06287e">T</span><span style="color:#666">()</span> <span style="color:#666">{}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">static</span> T <span style="color:#06287e">getInstance</span> <span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>instance<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>            instance
</span></span><span style="display:flex;"><span>        <span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">else</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>            <span style="color:#007020;font-weight:bold">synchronized</span><span style="color:#666">(</span>T<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>instance<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                    instance
</span></span><span style="display:flex;"><span>                <span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">else</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>                    instance <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> T <span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>                <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>            <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Lazy or not, once again, to access the instance, simply do <code>T.instance</code> (property access, shorcut for <code>T.getInstance()</code>).</p>
<h3 id="immutable">@Immutable</h3>
<p>Immutable objects are ones which don&rsquo;t change after initial creation. Such objects are frequently desirable because they are simple and can be safely shared even in multi-threading contexts. This makes them great for functional and concurrent scenarios. The rules for creating such objects are well-known:</p>
<ul>
<li>No mutators (methods that modify internal state)</li>
<li>Class must be final</li>
<li>Fields must be private and final</li>
<li>Defensive copying of mutable components</li>
<li><code>equals()</code>, <code>hashCode()</code> and <code>toString()</code> must be implemented in terms of the fields if you want to compare your objects or use them as keys in e.g. maps</li>
</ul>
<p>Instead of writing a very long Java or Groovy class mimicking this immutability behavior, Groovy lets you just write an immutable class as follow:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Immutable</span> <span style="color:#007020;font-weight:bold">final</span> <span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Coordinates</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    Double latitude<span style="color:#666">,</span> longitude
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> c1 <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Coordinates<span style="color:#666">(</span><span style="color:#002070;font-weight:bold">latitude:</span> <span style="color:#40a070">48.824068</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">longitude:</span> <span style="color:#40a070">2.531733</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> c2 <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Coordinates<span style="color:#666">(</span><span style="color:#40a070">48.824068</span><span style="color:#666">,</span> <span style="color:#40a070">2.531733</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> c1 <span style="color:#666">==</span> c2
</span></span></code></pre></div><p>All the boiler-plate code is generated at compile-time for you! The example shows that to instantiate such immutable coordinates, you can use one of the two constructors created by the transformation, one taking a map whose keys are the properties to set to the values associated with those keys, and the other taking the values of the properties as parameters. The <code>assert</code> also shows that <code>equals()</code> was implemented and allows us to properly compare such immutable objects.</p>
<p>You can have a look at the <a href="http://groovy.codehaus.org/Immutable+AST+Macro">details of the implementation</a> of this transformation. For the record, the Groovy example above using the <code>@Immutable</code> transformation is over 50 lines of equivalent Java code.</p>
<h3 id="lazy">@Lazy</h3>
<p>Another transformation is <code>@Lazy</code>. Sometimes, you want to handle the initialization of a field of your clas lazily, so that its value is computed only on first use, often because it may be time-consuming or memory-expensive to create. The usual approach is to customize the getter of said field, so that it takes care of the initialization when the getter is called the first time. But in Groovy 1.6, you can now use the <code>@Lazy</code> annotation for that purpose:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Person</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Lazy</span> pets <span style="color:#666">=</span> <span style="color:#666">[</span><span style="color:#4070a0">&#39;Cat&#39;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#39;Dog&#39;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#39;Bird&#39;</span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> p <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Person<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#666">!(</span>p<span style="color:#666">.</span><span style="color:#4070a0">dump</span><span style="color:#666">().</span><span style="color:#4070a0">contains</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;Cat&#39;</span><span style="color:#666">))</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> p<span style="color:#666">.</span><span style="color:#4070a0">pets</span><span style="color:#666">.</span><span style="color:#4070a0">size</span><span style="color:#666">()</span> <span style="color:#666">==</span> <span style="color:#40a070">3</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> p<span style="color:#666">.</span><span style="color:#4070a0">dump</span><span style="color:#666">().</span><span style="color:#4070a0">contains</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;Cat&#39;</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>In the case of complex computation for initializing the field, you may need to call some method for doing the work, instead of a value like our pets list. This is then possible to have the lazy evaluation being done by a closure call, as the following example shows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Person</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Lazy</span> List pets <span style="color:#666">=</span> <span style="color:#666">{</span> <span style="color:#60a0b0;font-style:italic">/* complex computation here */</span> <span style="color:#666">}()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>There is also an option for leveraging Soft references for garbage collection friendliness for expensive data structures that may be contained by such lazy fields:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Person</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Lazy</span><span style="color:#666">(</span>soft <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">true</span><span style="color:#666">)</span> List pets <span style="color:#666">=</span> <span style="color:#666">[</span><span style="color:#4070a0">&#39;Cat&#39;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#39;Dog&#39;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#39;Bird&#39;</span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> p <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Person<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> p<span style="color:#666">.</span><span style="color:#4070a0">pets</span><span style="color:#666">.</span><span style="color:#4070a0">contains</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;Cat&#39;</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>The internal field created by the compiler for <code>pets</code> will actually be a Soft reference, but accessing <code>p.pets</code> directly will return the value (ie. the list of pets) held by that reference, making the use of the soft reference transparent to the user of that class.</p>
<h3 id="delegate">@Delegate</h3>
<p>Java doesn&rsquo;t provide any built-in delegation mechanism, and so far Groovy didn&rsquo;t either. But with the <code>@Delegate</code> transformation, a class field or property can be annotated and become an object to which method calls are delegated. In the following example, an <code>Event</code> class has a date delegate, and the compiler will delegate all of <code>Date</code>&rsquo;s methods invoked on the <code>Event</code> class to the <code>Date</code> delegate. As shown in the latest <code>assert</code>, the <code>Event</code> class has got a <code>before(Date)</code> method, and all of <code>Date</code>&rsquo;s methods.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">java.text.SimpleDateFormat</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Event</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Delegate</span> Date when
</span></span><span style="display:flex;"><span>    String title<span style="color:#666">,</span> url
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> df <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> SimpleDateFormat<span style="color:#666">(</span><span style="color:#4070a0">&#34;yyyy/MM/dd&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> gr8conf <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Event<span style="color:#666">(</span><span style="color:#002070;font-weight:bold">title:</span> <span style="color:#4070a0">&#34;GR8 Conference&#34;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>                          <span style="color:#002070;font-weight:bold">url:</span> <span style="color:#4070a0">&#34;[http://www.gr8conf.org](http://www.gr8conf.org/)&#34;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>                         <span style="color:#002070;font-weight:bold">when:</span> df<span style="color:#666">.</span><span style="color:#4070a0">parse</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;2009/05/18&#34;</span><span style="color:#666">))</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> javaOne <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Event<span style="color:#666">(</span><span style="color:#002070;font-weight:bold">title:</span> <span style="color:#4070a0">&#34;JavaOne&#34;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>                          <span style="color:#002070;font-weight:bold">url:</span> <span style="color:#4070a0">&#34;&lt;http://java.sun.com/javaone/&gt;&#34;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>                         <span style="color:#002070;font-weight:bold">when:</span> df<span style="color:#666">.</span><span style="color:#4070a0">parse</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;2009/06/02&#34;</span><span style="color:#666">))</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> gr8conf<span style="color:#666">.</span><span style="color:#4070a0">before</span><span style="color:#666">(</span>javaOne<span style="color:#666">.</span><span style="color:#4070a0">when</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>The Groovy compiler adds all of <code>Date</code>&rsquo;s methods to the <code>Event</code> class, and those methods simply delegate the call to the <code>Date</code> field. If the delegate is not a final class, it is even possible to make the <code>Event</code> class a subclass of <code>Date</code> simply by extending <code>Date</code>, as shown below. No need to implement the delegation ourselves by adding each and every <code>Date</code> methods to our <code>Event</code> class, since the compiler is friendly-enough with us to do the job itself.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Event</span> <span style="color:#007020;font-weight:bold">extends</span> Date <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Delegate</span> Date when
</span></span><span style="display:flex;"><span>    String title<span style="color:#666">,</span> url
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>In the case you are delegating to an interface, however, you don&rsquo;t even need to explictely say you implement the interface of the delegate. The <code>@Delegate</code> transformation will take care of this and implement that interface. So the instances of your class will automatically be <code>instanceof</code> the delegate&rsquo;s interface.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">java.util.concurrent.locks.*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">LockableList</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Delegate</span> <span style="color:#007020;font-weight:bold">private</span> List list <span style="color:#666">=</span> <span style="color:#666">[]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Delegate</span> <span style="color:#007020;font-weight:bold">private</span> Lock lock <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> ReentrantLock<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> list <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> LockableList<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>list<span style="color:#666">.</span><span style="color:#4070a0">lock</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    list <span style="color:#666">&lt;&lt;</span> <span style="color:#4070a0">&#39;Groovy&#39;</span>
</span></span><span style="display:flex;"><span>    list <span style="color:#666">&lt;&lt;</span> <span style="color:#4070a0">&#39;Grails&#39;</span>
</span></span><span style="display:flex;"><span>    list <span style="color:#666">&lt;&lt;</span> <span style="color:#4070a0">&#39;Griffon&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">finally</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    list<span style="color:#666">.</span><span style="color:#4070a0">unlock</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> list<span style="color:#666">.</span><span style="color:#4070a0">size</span><span style="color:#666">()</span> <span style="color:#666">==</span> <span style="color:#40a070">3</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> list <span style="color:#007020;font-weight:bold">instanceof</span> Lock
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> list <span style="color:#007020;font-weight:bold">instanceof</span> List
</span></span></code></pre></div><p>In this example, our <code>LockableList</code> is now a composite of a list and a lock and is <code>instanceof</code> of <code>List</code> and <code>Lock</code>. However, if you didn&rsquo;t intend your class to be implementing these interfaces, you would still be able to do so by specifying a parameter on the annotation:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Delegate</span><span style="color:#666">(</span>interfaces <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">false</span><span style="color:#666">)</span> <span style="color:#007020;font-weight:bold">private</span> List list <span style="color:#666">=</span> <span style="color:#666">[]</span>
</span></span></code></pre></div><h3 id="newify">@Newify</h3>
<p>The <code>@Newify</code> transformation proposes two new ways of instantiating classes. The first one is providing Ruby like approach to creating instances with a <code>new()</code> class method:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Newify</span> rubyLikeNew<span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">assert</span> Integer<span style="color:#666">.</span><span style="color:#4070a0">new</span><span style="color:#666">(</span><span style="color:#40a070">42</span><span style="color:#666">)</span> <span style="color:#666">==</span> <span style="color:#40a070">42</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>rubyLikeNew<span style="color:#666">()</span>
</span></span></code></pre></div><p>But it is also possible to follow the Python approach with omitting the <code>new</code> keyword. Imagine the following tree creation:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Tree</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> elements
</span></span><span style="display:flex;"><span>    <span style="color:#06287e">Tree</span><span style="color:#666">(</span>Object<span style="color:#666">...</span> elements<span style="color:#666">)</span> <span style="color:#666">{</span> <span style="color:#007020;font-weight:bold">this</span><span style="color:#666">.</span><span style="color:#4070a0">elements</span> <span style="color:#666">=</span> elements <span style="color:#666">**</span><span style="color:#007020;font-weight:bold">as</span><span style="color:#666">**</span> List <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Leaf</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> value
</span></span><span style="display:flex;"><span>    <span style="color:#06287e">Leaf</span><span style="color:#666">(</span>value<span style="color:#666">)</span> <span style="color:#666">{</span> <span style="color:#007020;font-weight:bold">this</span><span style="color:#666">.</span><span style="color:#4070a0">value</span> <span style="color:#666">=</span> value <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> <span style="color:#06287e">buildTree</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">Tree</span><span style="color:#666">(</span><span style="color:#007020;font-weight:bold">new</span> Tree<span style="color:#666">(</span><span style="color:#007020;font-weight:bold">new</span> Leaf<span style="color:#666">(</span><span style="color:#40a070">1</span><span style="color:#666">),</span> <span style="color:#007020;font-weight:bold">new</span> Leaf<span style="color:#666">(</span><span style="color:#40a070">2</span><span style="color:#666">)),</span> <span style="color:#007020;font-weight:bold">new</span> Leaf<span style="color:#666">(</span><span style="color:#40a070">3</span><span style="color:#666">))</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>buildTree<span style="color:#666">()</span>
</span></span></code></pre></div><p>The creation of the tree is not very readable because of all those <code>new</code> keywords spread across the line. The Ruby approach wouldn&rsquo;t be more readable, since a <code>new()</code> method call for creating each element is needed. But by using <code>@Newify</code>, we can improve our tree building slightly to make it easier on the eye:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Newify</span><span style="color:#666">([</span>Tree<span style="color:#666">,</span> Leaf<span style="color:#666">])</span> buildTree<span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    Tree<span style="color:#666">(</span>Tree<span style="color:#666">(</span>Leaf<span style="color:#666">(</span><span style="color:#40a070">1</span><span style="color:#666">),</span> Leaf<span style="color:#666">(</span><span style="color:#40a070">2</span><span style="color:#666">)),</span> Leaf<span style="color:#666">(</span><span style="color:#40a070">3</span><span style="color:#666">))</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>You&rsquo;ll also notice that we just allowed <code>Tree</code> and <code>Leaf</code> to be <em>newified</em>. By default, under the scope which is annotated, all instantiations are <em>newified</em>, but you can limit the reach by specifying the classes you&rsquo;re interested in. Also, note that for our example, perhaps a Groovy builder may have been more appropriate, since its purpose is to indeed create any kind of hierarchical / tree strucutre.</p>
<p>If we take another look at our coordinates example from a few sections earlier, using both <code>@Immutable</code> and <code>@Newify</code> can be interesting for creating a path with a concise but type-safe manner:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Immutable</span> <span style="color:#007020;font-weight:bold">final</span> <span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Coordinates</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    Double latitude<span style="color:#666">,</span> longitude
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Immutable</span> <span style="color:#007020;font-weight:bold">final</span> <span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Path</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    Coordinates<span style="color:#666">[]</span> coordinates
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Newify</span><span style="color:#666">([</span>Coordinates<span style="color:#666">,</span> Path<span style="color:#666">])</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> <span style="color:#06287e">build</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    Path<span style="color:#666">(</span>
</span></span><span style="display:flex;"><span>        Coordinates<span style="color:#666">(</span><span style="color:#40a070">48.824068</span><span style="color:#666">,</span> <span style="color:#40a070">2.531733</span><span style="color:#666">),</span>
</span></span><span style="display:flex;"><span>        Coordinates<span style="color:#666">(</span><span style="color:#40a070">48.857840</span><span style="color:#666">,</span> <span style="color:#40a070">2.347212</span><span style="color:#666">),</span>
</span></span><span style="display:flex;"><span>        Coordinates<span style="color:#666">(</span><span style="color:#40a070">48.858429</span><span style="color:#666">,</span> <span style="color:#40a070">2.342622</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">build</span><span style="color:#666">().</span><span style="color:#4070a0">coordinates</span><span style="color:#666">.</span><span style="color:#4070a0">size</span><span style="color:#666">()</span> <span style="color:#666">==</span> <span style="color:#40a070">3</span>
</span></span></code></pre></div><p>A closing remark here: since a <code>Path(Coordinates[] coordinates)</code> was generated, we can use that constructor in a <em>varargs way</em> in Groovy, just as if it had been defined as <code>Path(Coordinates... coordinates)</code>.</p>
<h3 id="category-and-mixin">@Category and @Mixin</h3>
<p>If you&rsquo;ve been using Groovy for a while, you&rsquo;re certainly familiar with the concept of Categories. It&rsquo;s a mechanism to extend existing types (even final classes from the JDK or third-party libraries), to add new methods to them. This is also a technique which can be used when writing Domain-Specific Languages. Let&rsquo;s consider the example below:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">final</span> <span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Distance</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> number
</span></span><span style="display:flex;"><span>    String <span style="color:#06287e">toString</span><span style="color:#666">()</span> <span style="color:#666">{</span> <span style="color:#4070a0">&#34;${number}m&#34;</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">NumberCategory</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">static</span> Distance <span style="color:#06287e">getMeters</span><span style="color:#666">(</span>Number self<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">Distance</span><span style="color:#666">(</span><span style="color:#002070;font-weight:bold">number:</span> self<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>use<span style="color:#666">(</span>NumberCategory<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> dist <span style="color:#666">=</span> <span style="color:#40a070">300</span><span style="color:#666">.</span><span style="color:#4070a0">meters</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">assert</span> dist <span style="color:#007020;font-weight:bold">instanceof</span> Distance
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">assert</span> dist<span style="color:#666">.</span><span style="color:#4070a0">toString</span><span style="color:#666">()</span> <span style="color:#666">==</span> <span style="color:#4070a0">&#34;300m&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>We have a simplistic and fictive <code>Distance</code> class which may have been provided by a third-party, who had the bad idea of making the class <code>final</code> so that nobody could ever extend it in any way. But thanks to a Groovy Category, we are able to decorate the <code>Distance</code> type with additional methods. Here, we&rsquo;re going to add a <code>getMeters()</code> method to numbers, by actually decorating the <code>Number</code> type. By adding a getter to a number, you&rsquo;re able to reference it using the nice property syntax of Groovy. So instead of writing <code>300.getMeters()</code>, you&rsquo;re able to write <code>300.meters</code>.</p>
<p>The downside of this category system and notation is that to add instance methods to other types, you have to create <code>static</code> methods, and furthermore, there&rsquo;s a first argument which represents the instance of the type we&rsquo;re working on. The other arguments are the normal arguments the method will take as parameters. So it may be a bit less intuitive than a normal method definition we would have added to <code>Distance</code>, should we have had access to its source code for enhancing it. Here comes the <code>@Category</code> annotation, which transforms a class with instance methods into a Groovy category:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Category</span><span style="color:#666">(</span>Number<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">NumberCategory</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    Distance <span style="color:#06287e">getMeters</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">Distance</span><span style="color:#666">(</span><span style="color:#002070;font-weight:bold">number:</span> <span style="color:#007020;font-weight:bold">this</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>No need for declaring the methods <code>static</code>, and the <code>this</code> you use here is actually the number on which the category will apply, it&rsquo;s not the real <code>this</code> of the category instance should we create one. Then to use the category, you can continue to use the <code>use(Category) {}</code> construct. What you&rsquo;ll notice however is that these kind of categories only apply to one single type at a time, unlike classical categories which can be applied to any number of types.</p>
<p>Now, pair <code>@Category</code> extensions to the <code>@Mixin</code> transformation, and you can mix in various behavior in a class, with an approach similar to multiple inheritance:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Category</span><span style="color:#666">(</span>Vehicle<span style="color:#666">)</span> <span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">FlyingAbility</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> <span style="color:#06287e">fly</span><span style="color:#666">()</span> <span style="color:#666">{</span> <span style="color:#4070a0">&#34;I&#39;m the ${name} and I fly!&#34;</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Category</span><span style="color:#666">(</span>Vehicle<span style="color:#666">)</span> <span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">DivingAbility</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> <span style="color:#06287e">dive</span><span style="color:#666">()</span> <span style="color:#666">{</span> <span style="color:#4070a0">&#34;I&#39;m the ${name} and I dive!&#34;</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">interface</span> <span style="color:#0e84b5;font-weight:bold">Vehicle</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    String <span style="color:#06287e">getName</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Mixin</span><span style="color:#666">(</span>DivingAbility<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Submarine</span> <span style="color:#007020;font-weight:bold">implements</span> Vehicle <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    String <span style="color:#06287e">getName</span><span style="color:#666">()</span> <span style="color:#666">{</span> <span style="color:#4070a0">&#34;Yellow Submarine&#34;</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Mixin</span><span style="color:#666">(</span>FlyingAbility<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Plane</span> <span style="color:#007020;font-weight:bold">implements</span> Vehicle <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    String <span style="color:#06287e">getName</span><span style="color:#666">()</span> <span style="color:#666">{</span> <span style="color:#4070a0">&#34;Concorde&#34;</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Mixin</span><span style="color:#666">([</span>DivingAbility<span style="color:#666">,</span> FlyingAbility<span style="color:#666">])</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">JamesBondVehicle</span> <span style="color:#007020;font-weight:bold">implements</span> Vehicle <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    String <span style="color:#06287e">getName</span><span style="color:#666">()</span> <span style="color:#666">{</span> <span style="color:#4070a0">&#34;James Bond&#39;s vehicle&#34;</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">Plane</span><span style="color:#666">().</span><span style="color:#4070a0">fly</span><span style="color:#666">()</span> <span style="color:#666">==</span>
</span></span><span style="display:flex;"><span>       <span style="color:#4070a0">&#34;I&#39;m the Concorde and I fly!&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">Submarine</span><span style="color:#666">().</span><span style="color:#4070a0">dive</span><span style="color:#666">()</span> <span style="color:#666">==</span>
</span></span><span style="display:flex;"><span>       <span style="color:#4070a0">&#34;I&#39;m the Yellow Submarine and I dive!&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">JamesBondVehicle</span><span style="color:#666">().</span><span style="color:#4070a0">fly</span><span style="color:#666">()</span> <span style="color:#666">==</span>
</span></span><span style="display:flex;"><span>       <span style="color:#4070a0">&#34;I&#39;m the James Bond&#39;s vehicle and I fly!&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">JamesBondVehicle</span><span style="color:#666">().</span><span style="color:#4070a0">dive</span><span style="color:#666">()</span> <span style="color:#666">==</span>
</span></span><span style="display:flex;"><span>       <span style="color:#4070a0">&#34;I&#39;m the James Bond&#39;s vehicle and I dive!&#34;</span>
</span></span></code></pre></div><p>You don&rsquo;t inherit from various interfaces and inject the same behavior in each subclass, instead you mixin the categories into your class. Here, our marvelous James Bond vehicle gets the flying and diving capabilities through mixins.</p>
<p>An important point to make here is that unlike <code>@Delegate</code> which can <em>inject</em> interfaces into the class in which the delegate is declared, <code>@Mixin</code> just does runtime mixing &mdash; as we shall see in the metaprogramming enhancements further down in this article.</p>
<h3 id="packagescope">@PackageScope</h3>
<p>Groovy&rsquo;s convention for properties is that any <em>field</em> without any visibility modifier is exposed as a property, with a getter and a setter transparently generated for you. For instance, this <code>Person</code> class exposes a getter <code>getName()</code> and a setter <code>setName()</code> for a private <code>name</code> field:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Person</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    String name
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Which is equivalent to this Java class:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">public</span> <span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Person</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">private</span> String name<span style="color:#666">;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">public</span> String <span style="color:#06287e">getName</span><span style="color:#666">()</span> <span style="color:#666">{</span> <span style="color:#007020;font-weight:bold">return</span> name<span style="color:#666">;</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">public</span> <span style="color:#902000">void</span> <span style="color:#06287e">setName</span><span style="color:#666">(</span>name<span style="color:#666">)</span> <span style="color:#666">{</span> <span style="color:#007020;font-weight:bold">this</span><span style="color:#666">.</span><span style="color:#4070a0">name</span> <span style="color:#666">=</span> name<span style="color:#666">;</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>That said, this approach has one drawback in that you don&rsquo;t have the possibility to define a field with package-scope visibility. To be able to expose a field with package-scope visibility, you can now annotate your field with the <code>@PackageScope</code> annotation.</p>
<h2 id="grape-the-groovy-adaptable--advanced-packaging-engine">Grape, the Groovy Adaptable / Advanced Packaging Engine</h2>
<p>To continue our overview of the AST transformations, we&rsquo;ll now learn more about Grape, a mechanism to add and leverage dependencies in your Groovy scripts. Groovy scripts can require certain libraries: by explicitly saying so in your script with the <code>@Grab</code> transformation or with the <code>Grape.grab()</code> method call, the runtime will find the needed JARs for you. With Grape, you can easily distribute scripts without their dependencies, and have them downloaded on first use of your script and cached. Under the hood, Grape uses Ivy and Maven repositories containing the libraries you may need in your scripts.</p>
<p>Imagine you want to get the links of all the PDF documents referenced by the Java 5 documentation. You want to parse the HTML page as if it were an XML-compliant document (which it is not) with the Groovy <code>XmlParser</code>, so you can use the TagSoup SAX-compliant parser which transforms HTML into well-formed valid XML. You don&rsquo;t even have to mess up with your classpath when running your script, just <em>grab</em> the TagSoup library through Grape:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.ccil.cowan.tagsoup.Parser</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// find the PDF links in the Java 1.5.0 documentation
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#555;font-weight:bold">@Grab</span><span style="color:#666">(</span>group<span style="color:#666">=</span><span style="color:#4070a0">&#39;org.ccil.cowan.tagsoup&#39;</span><span style="color:#666">,</span> module<span style="color:#666">=</span><span style="color:#4070a0">&#39;tagsoup&#39;</span><span style="color:#666">,</span> version<span style="color:#666">=</span><span style="color:#4070a0">&#39;0.9.7&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> <span style="color:#06287e">getHtml</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> tagsoupParser <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Parser<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> parser <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> XmlParser<span style="color:#666">(</span>tagsoupParser<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    parser<span style="color:#666">.</span><span style="color:#4070a0">parse</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;http://java.sun.com/j2se/1.5.0/download-pdf.html&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>html<span style="color:#666">.</span><span style="color:#4070a0">body</span><span style="color:#666">.</span><span style="color:#4070a0">&#39;**&#39;</span><span style="color:#666">.</span><span style="color:#4070a0">a</span><span style="color:#666">.</span><span style="color:#555;font-weight:bold">@href.grep</span><span style="color:#666">(~</span><span style="color:#4070a0">/.*\.pdf/</span><span style="color:#666">).</span><span style="color:#4070a0">each</span><span style="color:#666">{</span> println it <span style="color:#666">}</span>
</span></span></code></pre></div><p>For the pleasure of giving another example: let&rsquo;s use the <a href="http://www.mortbay.org/jetty/">Jetty servlet container</a> to expose <a href="http://groovy.codehaus.org/Groovy+Templates">Groovy templates</a> in a few lines of code:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.mortbay.jetty.Server</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.mortbay.jetty.servlet.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.servlet.*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Grab</span><span style="color:#666">(</span>group <span style="color:#666">=</span> <span style="color:#4070a0">&#39;org.mortbay.jetty&#39;</span><span style="color:#666">,</span> module <span style="color:#666">=</span> <span style="color:#4070a0">&#39;jetty-embedded&#39;</span><span style="color:#666">,</span> version <span style="color:#666">=</span> <span style="color:#4070a0">&#39;6.1.0&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> <span style="color:#06287e">runServer</span><span style="color:#666">(</span>duration<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> server <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Server<span style="color:#666">(</span><span style="color:#40a070">8080</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> context <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Context<span style="color:#666">(</span>server<span style="color:#666">,</span> <span style="color:#4070a0">&#34;/&#34;</span><span style="color:#666">,</span> Context<span style="color:#666">.</span><span style="color:#4070a0">SESSIONS</span><span style="color:#666">);</span>
</span></span><span style="display:flex;"><span>    context<span style="color:#666">.</span><span style="color:#4070a0">resourceBase</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#34;.&#34;</span>
</span></span><span style="display:flex;"><span>    context<span style="color:#666">.</span><span style="color:#4070a0">addServlet</span><span style="color:#666">(</span>TemplateServlet<span style="color:#666">,</span> <span style="color:#4070a0">&#34;*.gsp&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    server<span style="color:#666">.</span><span style="color:#4070a0">start</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    sleep duration
</span></span><span style="display:flex;"><span>    server<span style="color:#666">.</span><span style="color:#4070a0">stop</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>runServer<span style="color:#666">(</span><span style="color:#40a070">10000</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>Grape will download Jetty and its dependencies on first launch of this script, and cache them. We&rsquo;re creating a new Jetty <code>Server</code> on port 8080, then expose Groovy&rsquo;s <code>TemplateServlet</code> at the root of the context &mdash; Groovy comes with its own powerful template engine mechanism. We start the server and let it run for a certain duration. Each time someone will hit <code>http://localhost:8080/somepage.gsp</code>, it will display the <code>somepage.gsp</code> template to the user &mdash; those template pages should be situated in the same directory as this server script.</p>
<p>Grape can also be used as a method call instead of as an annotation. You can also install, list, resolve dependencies from the command-line using the <code>grape</code> command. For <a href="http://groovy.codehaus.org/Grape">more information on Grape</a>, please refer to the documentation.</p>
<h2 id="swing-builder-improvements">Swing builder improvements</h2>
<p>To wrap up our overview of AST transformations, let&rsquo;s finish by speaking about two transformations very useful to Swing developers: <code>@Bindable</code> and <code>@Vetoable</code>. When creating Swing UIs, you&rsquo;re often interested in monitoring the changes of value of certain UI elements. For this purpose, the usual approach is to use JavaBeans <code>PropertyChangeListener</code>s to be notified when the value of a class field changes. You then end up writing this very common boiler-plate code in your Java beans:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.beans.PropertyChangeSupport</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.beans.PropertyChangeListener</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">MyBean</span><span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span>prop;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>PropertyChangeSupport<span style="color:#bbb"> </span>pcs<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>PropertyChangeSupport(<span style="color:#007020;font-weight:bold">this</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">addPropertyChangeListener</span>(PropertyChangeListener<span style="color:#bbb"> </span>l)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>pcs.<span style="color:#4070a0">add</span>(l);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">removePropertyChangeListener</span>(PropertyChangeListener<span style="color:#bbb"> </span>l)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>pcs.<span style="color:#4070a0">remove</span>(l);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">getProp</span>()<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>prop;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">setProp</span>(String<span style="color:#bbb"> </span>prop)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>pcs.<span style="color:#4070a0">firePropertyChanged</span>(<span style="color:#4070a0">&#34;prop&#34;</span>,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">this</span>.<span style="color:#4070a0">prop</span>,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">this</span>.<span style="color:#4070a0">prop</span><span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>prop);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Fortunately, with Groovy and the <code>@Bindable</code> annotation, this code can be greatly simplified:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">MyBean</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Bindable</span> String prop
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Now pair that with Groovy&rsquo;s Swing builder new <code>bind()</code> method, define a text field and bind its value to a property of your data model:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>textField <span style="color:#002070;font-weight:bold">text:</span> bind<span style="color:#666">(</span><span style="color:#002070;font-weight:bold">source:</span> myBeanInstance<span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">sourceProperty:</span> <span style="color:#4070a0">&#39;prop&#39;</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>Or even:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>textField <span style="color:#002070;font-weight:bold">text:</span> bind <span style="color:#666">{</span> myBeanInstance<span style="color:#666">.</span><span style="color:#4070a0">prop</span> <span style="color:#666">}</span>
</span></span></code></pre></div><p>The binding also works with simple expressions in the closure, for instance something like this is possible too:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>bean <span style="color:#002070;font-weight:bold">location:</span> bind <span style="color:#666">{</span> pos<span style="color:#666">.</span><span style="color:#4070a0">x</span> <span style="color:#666">+</span> <span style="color:#4070a0">&#39;, &#39;</span> <span style="color:#666">+</span> pos<span style="color:#666">.</span><span style="color:#4070a0">y</span> <span style="color:#666">}</span>
</span></span></code></pre></div><p>You may also be interested in having a look at <a href="http://groovy.codehaus.org/api/groovy/util/ObservableMap.html">ObservableMap</a> and <a href="http://groovy.codehaus.org/api/groovy/util/ObservableList.html">ObservableList</a>, for a similar mechanism on maps and lists.</p>
<p>Along with <code>@Bindable</code>, there&rsquo;s also a <code>@Vetoable</code> transformation for when you need to be able to veto some property change. Let&rsquo;s consider a <code>Trompetist</code> class, where the performer&rsquo;s name is not allowed to contain the letter &lsquo;z&rsquo;:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">java.beans.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.beans.Vetoable</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Trumpetist</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Vetoable</span> String name
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> me <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Trumpetist<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>me<span style="color:#666">.</span><span style="color:#4070a0">vetoableChange</span> <span style="color:#666">=</span> <span style="color:#666">{</span> PropertyChangeEvent pce <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>pce<span style="color:#666">.</span><span style="color:#4070a0">newValue</span><span style="color:#666">.</span><span style="color:#4070a0">contains</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;z&#39;</span><span style="color:#666">))</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">throw</span> <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">PropertyVetoException</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;The letter &#39;z&#39; is not allowed in a name&#34;</span><span style="color:#666">,</span> pce<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>me<span style="color:#666">.</span><span style="color:#4070a0">name</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Louis Armstrong&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    me<span style="color:#666">.</span><span style="color:#4070a0">name</span> <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Dizzy Gillespie&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">assert</span> <span style="color:#007020;font-weight:bold">false</span><span style="color:#666">:</span> <span style="color:#4070a0">&#34;You should not be able to set a name with letter &#39;z&#39; in it.&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">catch</span> <span style="color:#666">(</span>PropertyVetoException pve<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">assert</span> <span style="color:#007020;font-weight:bold">true</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Looking at a more thorough Swing builder example with binding:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.swing.SwingBuilder</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.beans.Bindable</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">static</span> javax<span style="color:#666">.</span><span style="color:#4070a0">swing</span><span style="color:#666">.</span><span style="color:#4070a0">JFrame</span><span style="color:#666">.</span><span style="color:#4070a0">EXIT_ON_CLOSE</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">TextModel</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Bindable</span> String text
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> textModel <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> TextModel<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>SwingBuilder<span style="color:#666">.</span><span style="color:#4070a0">build</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    frame<span style="color:#666">(</span> <span style="color:#002070;font-weight:bold">title:</span> <span style="color:#4070a0">&#39;Binding Example (Groovy)&#39;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">size:</span> <span style="color:#666">[</span><span style="color:#40a070">240</span><span style="color:#666">,</span><span style="color:#40a070">100</span><span style="color:#666">],</span> <span style="color:#002070;font-weight:bold">show:</span> <span style="color:#007020;font-weight:bold">true</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>          <span style="color:#002070;font-weight:bold">locationRelativeTo:</span> <span style="color:#007020;font-weight:bold">null</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">defaultCloseOperation:</span> EXIT_ON_CLOSE <span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        gridLayout <span style="color:#002070;font-weight:bold">cols:</span> <span style="color:#40a070">1</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">rows:</span> <span style="color:#40a070">2</span>
</span></span><span style="display:flex;"><span>        textField <span style="color:#002070;font-weight:bold">id:</span> <span style="color:#4070a0">&#39;textField&#39;</span>
</span></span><span style="display:flex;"><span>        bean textModel<span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">text:</span> bind<span style="color:#666">{</span> textField<span style="color:#666">.</span><span style="color:#4070a0">text</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>        label <span style="color:#002070;font-weight:bold">text:</span> bind<span style="color:#666">{</span> textModel<span style="color:#666">.</span><span style="color:#4070a0">text</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Running this script shows up the frame below with a text field and a lable below, and the label&rsquo;s text is bound on the text field&rsquo;s content.</p>
<p><figure>
  <a href="#img-35a12fd441e37b20f1abdf548aaf2ebe">
    <img src="https://imgopt.infoq.com/fit-in/1200x2400/filters:quality%2880%29/filters:no_upscale%28%29/articles/groovy-1-6/en/resources/bindable-example.png"
      alt="@Bindable Swing builder demo"
       />
  </a>
  <figcaption>@Bindable Swing builder demo</figcaption>
</figure>
<div class="lightbox" id="img-35a12fd441e37b20f1abdf548aaf2ebe">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://imgopt.infoq.com/fit-in/1200x2400/filters:quality%2880%29/filters:no_upscale%28%29/articles/groovy-1-6/en/resources/bindable-example.png"
    alt="@Bindable Swing builder demo"
     />
  <div class="lightbox-caption">@Bindable Swing builder demo</div>
</div>
</p>
<p>SwingBuilder has evolved so nicely in the past year that the Groovy Swing team decided to launch a new project based on it, and on the Grails foundations: project <a href="http://griffon.codehaus.org/">Griffon</a> was born. Griffon proposes to bring the <em>Convention over Configuration</em> paradigm of Grails, as well as all its project structure, plugin system, gant scripting capabilities, etc.</p>
<p>If you are developing Swing rich clients, make sure to have a look at <a href="http://griffon.codehaus.org/">Griffon</a>.</p>
<h2 id="swing-console-improvements">Swing console improvements</h2>
<p>Swinging along the topic of UIs, the Swing console has also evolved:</p>
<ul>
<li>
<p>The console can be run as an Applet (<code>groovy.ui.ConsoleApplet</code>).</p>
</li>
<li>
<p>Beyond syntax highlighting, the editor also supports code indentation.</p>
</li>
<li>
<p>Drag&rsquo;n droping a Groovy script over the text area will open the file.</p>
</li>
<li>
<p>You can modify the classpath with which the script in the console is being run, by adding a new JAR or a directory to the classpath as shown in the screenshot below.</p>
<p><figure>
  <a href="#img-0f9b3c6a0c6fe401e3a3420b6b69eb94">
    <img src="https://imgopt.infoq.com/fit-in/1200x2400/filters:quality%2880%29/filters:no_upscale%28%29/articles/groovy-1-6/en/resources/sc-add-jar.png"
      alt="Swing console: Add a JAR in the classpath"
       />
  </a>
  <figcaption>Swing console: Add a JAR in the classpath</figcaption>
</figure>
<div class="lightbox" id="img-0f9b3c6a0c6fe401e3a3420b6b69eb94">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://imgopt.infoq.com/fit-in/1200x2400/filters:quality%2880%29/filters:no_upscale%28%29/articles/groovy-1-6/en/resources/sc-add-jar.png"
    alt="Swing console: Add a JAR in the classpath"
     />
  <div class="lightbox-caption">Swing console: Add a JAR in the classpath</div>
</div>
</p>
</li>
<li>
<p>A couple options have been added to the view menu item: for showing the script being run in the output area, and for visualizing the execution results.</p>
<p><figure>
  <a href="#img-77323194f11501924aaf97bad3a7c84a">
    <img src="https://imgopt.infoq.com/fit-in/1200x2400/filters:quality%2880%29/filters:no_upscale%28%29/articles/groovy-1-6/en/resources/sc-options.png"
      alt="Swing console: visualization options"
       />
  </a>
  <figcaption>Swing console: visualization options</figcaption>
</figure>
<div class="lightbox" id="img-77323194f11501924aaf97bad3a7c84a">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://imgopt.infoq.com/fit-in/1200x2400/filters:quality%2880%29/filters:no_upscale%28%29/articles/groovy-1-6/en/resources/sc-options.png"
    alt="Swing console: visualization options"
     />
  <div class="lightbox-caption">Swing console: visualization options</div>
</div>
</p>
</li>
<li>
<p>When an exception is thrown in your script, the lines of the stacktrace relative to your script are clickable, for easy navigation to the point where the error occurred.</p>
<p><figure>
  <a href="#img-145b9998fa3d0d92cf4c48979c746414">
    <img src="https://imgopt.infoq.com/fit-in/1200x2400/filters:quality%2880%29/filters:no_upscale%28%29/articles/groovy-1-6/en/resources/sc-click-stack.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-145b9998fa3d0d92cf4c48979c746414">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://imgopt.infoq.com/fit-in/1200x2400/filters:quality%2880%29/filters:no_upscale%28%29/articles/groovy-1-6/en/resources/sc-click-stack.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
</li>
<li>
<p>Also, when your script contains compilation errors, the error messages are clickable too.</p>
<p><figure>
  <a href="#img-83370b5d59c9a16e5a437f1faf39cce1">
    <img src="https://imgopt.infoq.com/fit-in/1200x2400/filters:quality%2880%29/filters:no_upscale%28%29/articles/groovy-1-6/en/resources/sc-click-comp-error.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-83370b5d59c9a16e5a437f1faf39cce1">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://imgopt.infoq.com/fit-in/1200x2400/filters:quality%2880%29/filters:no_upscale%28%29/articles/groovy-1-6/en/resources/sc-click-comp-error.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
</li>
</ul>
<p>Back on the visualization of the results in the script output area, a fun system was added to let you customize how certain results are rendered. When you execute a script returning a map of Jazz musicians, you may see something like this in your console:</p>
<p><figure>
  <a href="#img-7bb1be2e1792fe5e7501e43fede868d9">
    <img src="https://imgopt.infoq.com/fit-in/1200x2400/filters:quality%2880%29/filters:no_upscale%28%29/articles/groovy-1-6/en/resources/sc-without-visu.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-7bb1be2e1792fe5e7501e43fede868d9">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://imgopt.infoq.com/fit-in/1200x2400/filters:quality%2880%29/filters:no_upscale%28%29/articles/groovy-1-6/en/resources/sc-without-visu.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>What you see here is the usual textual representation of a <code>Map</code>. But, what if we enabled custom visualization of certain results? The Swing console allows you to do just that. First of all, you have to ensure that the visualization option is ticked: <code>View -&gt; Visualize Script Results</code> &mdash; for the record, all settings of the Groovy Console are stored and remembered thanks to the Preference API. There are a few result visualizations built-in: if the script returns a <code>java.awt.Image</code>, a <code>javax.swing.Icon</code>, or a <code>java.awt.Component</code> with no parent, the object is displayed instead of its <code>toString()</code> representation. Otherwise, everything else is still just represented as text. Now, create the following Groovy script in <code>~/.groovy/OutputTransforms.groovy</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">javax.swing.*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>transforms <span style="color:#666">&lt;&lt;</span> <span style="color:#666">{</span> result <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>result <span style="color:#007020;font-weight:bold">instanceof</span> Map<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#902000">def</span> table <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> JTable<span style="color:#666">(</span>
</span></span><span style="display:flex;"><span>            result<span style="color:#666">.</span><span style="color:#4070a0">collect</span><span style="color:#666">{</span> k<span style="color:#666">,</span> v <span style="color:#666">-&lt;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#666">[</span>k<span style="color:#666">,</span> v<span style="color:#666">?.</span><span style="color:#4070a0">inspect</span><span style="color:#666">()]</span> <span style="color:#666">**</span><span style="color:#007020;font-weight:bold">as</span><span style="color:#666">**</span> Object<span style="color:#666">[]</span>
</span></span><span style="display:flex;"><span>            <span style="color:#666">}</span> <span style="color:#666">**</span><span style="color:#007020;font-weight:bold">as</span><span style="color:#666">**</span> Object<span style="color:#666">[][],</span>
</span></span><span style="display:flex;"><span>            <span style="color:#666">[</span><span style="color:#4070a0">&#39;Key&#39;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#39;Value&#39;</span><span style="color:#666">]</span> <span style="color:#666">**</span><span style="color:#007020;font-weight:bold">as</span><span style="color:#666">**</span> Object<span style="color:#666">[])</span>
</span></span><span style="display:flex;"><span>        table<span style="color:#666">.</span><span style="color:#4070a0">preferredViewportSize</span> <span style="color:#666">=</span> table<span style="color:#666">.</span><span style="color:#4070a0">preferredSize</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">return</span> <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">JScrollPane</span><span style="color:#666">(</span>table<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>The Groovy Swing console will execute that script on startup, injecting a <code>transforms</code> list in the binding of the script, so that you can add your own script results representations. In our case, we transform the <code>Map</code> into a nice-looking Swing <code>JTable</code>. And we&rsquo;re now able to visualize maps in a friendly and attractive fashion, as the screenshot below shows:<figure>
  <a href="#img-bb8e68d8a6a7acd03f474adbfed4635c">
    <img src="https://imgopt.infoq.com/fit-in/1200x2400/filters:quality%2880%29/filters:no_upscale%28%29/articles/groovy-1-6/en/resources/sc-with-visu.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-bb8e68d8a6a7acd03f474adbfed4635c">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="https://imgopt.infoq.com/fit-in/1200x2400/filters:quality%2880%29/filters:no_upscale%28%29/articles/groovy-1-6/en/resources/sc-with-visu.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>The Swing console is obviously not to be confused with a real full-blown IDE, but for daily scripting tasks, the console is a handy tool in your toolbox.</p>
<h2 id="metaprogramming-enhancements">Metaprogramming enhancements</h2>
<p>What makes Groovy a dynamic language is its Meta-Object Protocol and its concept of metaclasses which represent the runtime behavior of your classes and instances. In Groovy 1.6, we continue improving this dynamic runtime system, bringing several new capabilities into the mix.</p>
<h3 id="per-instance-metaclass-even-for-pojos">Per instance metaclass even for POJOs</h3>
<p>So far, Groovy POGOs (Plain Old Groovy Objects) could have a per-instance metaclass, but POJOs could only have one metaclass for all instances (ie. a per-class metaclass). This is now not the case anymore, as POJOs can have a per-instance metaclass too. Also, setting the metaclass property to null will restore the default metaclass.</p>
<h3 id="expandometaclass-dsl">ExpandoMetaClass DSL</h3>
<p>Initially developed under the <a href="http://grails.org/">Grails</a> umbrella and integrated back into Groovy 1.5, ExpandoMetaClass is a very handy way for changing the runtime behavior of your objects and classes, instead of writing full-blow <code>MetaClass</code> classes. Each time, we want to add / change several properties or methods of an existing type, there is too much of a repetition of <code>Type.metaClass.xxx</code>. Take for example this extract of a <a href="http://groovy.dzone.com/news/domain-specific-language-unit-">Unit manipulation DSL</a> dealing with operator overloading:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>Number<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">multiply</span> <span style="color:#666">=</span> <span style="color:#666">{</span> Amount amount <span style="color:#666">-&gt;</span> amount<span style="color:#666">.</span><span style="color:#4070a0">times</span><span style="color:#666">(</span>delegate<span style="color:#666">)</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>Number<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">div</span> <span style="color:#666">=</span>      <span style="color:#666">{</span> Amount amount <span style="color:#666">-&gt;</span> amount<span style="color:#666">.</span><span style="color:#4070a0">inverse</span><span style="color:#666">().</span><span style="color:#4070a0">times</span><span style="color:#666">(</span>delegate<span style="color:#666">)</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Amount<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">div</span> <span style="color:#666">=</span>      <span style="color:#666">{</span> Number factor <span style="color:#666">-&gt;</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">divide</span><span style="color:#666">(</span>factor<span style="color:#666">)</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>Amount<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">div</span> <span style="color:#666">=</span>      <span style="color:#666">{</span> Amount factor <span style="color:#666">-&gt;</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">divide</span><span style="color:#666">(</span>factor<span style="color:#666">)</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>Amount<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">multiply</span> <span style="color:#666">=</span> <span style="color:#666">{</span> Number factor <span style="color:#666">-&gt;</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">times</span><span style="color:#666">(</span>factor<span style="color:#666">)</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>Amount<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">power</span> <span style="color:#666">=</span>    <span style="color:#666">{</span> Number factor <span style="color:#666">-&gt;</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">pow</span><span style="color:#666">(</span>factor<span style="color:#666">)</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>Amount<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">negative</span> <span style="color:#666">=</span> <span style="color:#666">{</span> <span style="color:#666">-&gt;</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">opposite</span><span style="color:#666">()</span> <span style="color:#666">}</span>
</span></span></code></pre></div><p>The repetition, here, looks obvious. But with the ExpandoMetaClass DSL, we can streamline the code by regrouping the operators per type:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>Number<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    multiply <span style="color:#666">{</span> Amount amount <span style="color:#666">-&gt;</span> amount<span style="color:#666">.</span><span style="color:#4070a0">times</span><span style="color:#666">(</span>delegate<span style="color:#666">)</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    div      <span style="color:#666">{</span> Amount amount <span style="color:#666">-&gt;</span> amount<span style="color:#666">.</span><span style="color:#4070a0">inverse</span><span style="color:#666">().</span><span style="color:#4070a0">times</span><span style="color:#666">(</span>delegate<span style="color:#666">)</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Amount<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    div <span style="color:#666">&lt;&lt;</span>   <span style="color:#666">{</span> Number factor <span style="color:#666">-&gt;</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">divide</span><span style="color:#666">(</span>factor<span style="color:#666">)</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    div <span style="color:#666">&lt;&lt;</span>   <span style="color:#666">{</span> Amount factor <span style="color:#666">-&gt;</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">divide</span><span style="color:#666">(</span>factor<span style="color:#666">)</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    multiply <span style="color:#666">{</span> Number factor <span style="color:#666">-&gt;</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">times</span><span style="color:#666">(</span>factor<span style="color:#666">)</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    power    <span style="color:#666">{</span> Number factor <span style="color:#666">-&gt;</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">pow</span><span style="color:#666">(</span>factor<span style="color:#666">)</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    negative <span style="color:#666">{</span> <span style="color:#666">-&gt;</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">opposite</span><span style="color:#666">()</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>A <code>metaClass()</code> method takes a closure as single argument, containing the various definitions of the methods and properties, instead of repeating the <code>Type.metaClass</code> on each line. When there is just one method of a given name, use the pattern <code>methodName { /* closure */ }</code>, but when there are several, you should use the append operator and follow the patten <code>methodName &lt;&lt; { /* closure */ }</code>. Static methods can also be added through this mechanism, so instead of the classical approach:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// add a fqn() method to Class to get the fully
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// qualified name of the class (ie. simply Class#getName)
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>Class<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">static</span><span style="color:#666">.</span><span style="color:#4070a0">fqn</span> <span style="color:#666">=</span> <span style="color:#666">{</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">name</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> String<span style="color:#666">.</span><span style="color:#4070a0">fqn</span><span style="color:#666">()</span> <span style="color:#666">==</span> <span style="color:#4070a0">&#34;java.lang.String&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>You can now <span style="color:#007020;font-weight:bold">do</span><span style="color:#666">:</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Class<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#39;static&#39;</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        fqn <span style="color:#666">{</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">name</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Note here that you have to quote the <code>static</code> keyword, to avoid this construct to look like a static initializer. For one off method addition, the classical approach is obviously more concise, but when you have several methods to add, the EMC DSL makes sense.</p>
<p>The usual approach for adding properties to existing classes through ExpandoMetaClass is to add a getter and a setter as methods. For instance, say you want to add a method that counts the number of words in a text file, you could try this:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>File<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">getWordCount</span> <span style="color:#666">=</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    delegate<span style="color:#666">.</span><span style="color:#4070a0">text</span><span style="color:#666">.</span><span style="color:#4070a0">split</span><span style="color:#666">(</span><span style="color:#4070a0">/\w/</span><span style="color:#666">).</span><span style="color:#4070a0">size</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">File</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;myFile.txt&#39;</span><span style="color:#666">).</span><span style="color:#4070a0">wordCount</span>
</span></span></code></pre></div><p>When there is some logic inside the getter, this is certainly the best approach, but when you just want to have new properties holding simple values, through the ExpandoMetaClass DSL, it is possible to define them. In the following example, a <code>lastAccessed</code> property is added to a <code>Car</code> class &mdash; each instance will have its property. Whenever a method is called on that car, this property is updated with a newer timestamp.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Car</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">void</span> <span style="color:#06287e">turnOn</span><span style="color:#666">()</span> <span style="color:#666">{}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">void</span> <span style="color:#06287e">drive</span><span style="color:#666">()</span> <span style="color:#666">{}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">void</span> <span style="color:#06287e">turnOff</span><span style="color:#666">()</span> <span style="color:#666">{}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Car<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    lastAccessed <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">null</span>
</span></span><span style="display:flex;"><span>    invokeMethod <span style="color:#666">=</span> <span style="color:#666">{</span> String name<span style="color:#666">,</span> args <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#902000">def</span> metaMethod <span style="color:#666">=</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">getMetaMethod</span><span style="color:#666">(</span>name<span style="color:#666">,</span> args<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>metaMethod<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>            delegate<span style="color:#666">.</span><span style="color:#4070a0">lastAccessed</span> <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Date<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>            metaMethod<span style="color:#666">.</span><span style="color:#4070a0">doMethodInvoke</span><span style="color:#666">(</span>delegate<span style="color:#666">,</span> args<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">else</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>            <span style="color:#007020;font-weight:bold">throw</span> <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">MissingMethodException</span><span style="color:#666">(</span>name<span style="color:#666">,</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">class</span><span style="color:#666">,</span> args<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> car <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Car<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>println <span style="color:#4070a0">&#34;Last accessed: ${car.lastAccessed ?: &#39;Never&#39;}&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>car<span style="color:#666">.</span><span style="color:#4070a0">turnOn</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>println <span style="color:#4070a0">&#34;Last accessed: ${car.lastAccessed ?: &#39;Never&#39;}&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>car<span style="color:#666">.</span><span style="color:#4070a0">drive</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>sleep <span style="color:#40a070">1000</span>
</span></span><span style="display:flex;"><span>println <span style="color:#4070a0">&#34;Last accessed: ${car.lastAccessed ?: &#39;Never&#39;}&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>sleep <span style="color:#40a070">1000</span>
</span></span><span style="display:flex;"><span>car<span style="color:#666">.</span><span style="color:#4070a0">turnOff</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>println <span style="color:#4070a0">&#34;Last accessed: ${car.lastAccessed ?: &#39;Never&#39;}&#34;</span>
</span></span></code></pre></div><p>In our example, in the DSL, we access that property through the <code>delegate</code> of the closure, with <code>delegate.lastAccessed = new Date()</code>. And we intercept any method call thanks to <code>invokeMethod()</code>, delegating to the original method for the call, and throwing an exception in case the method doesn&rsquo;t exist. Later on, you can see by executing this script that <code>lastAccessed</code> is updated as soon as we call a method on our instance.</p>
<h3 id="runtime-mixins">Runtime mixins</h3>
<p>Last metaprogramming feature we&rsquo;ll cover today: runtime mixins. <code>@Mixin</code> allowed you to mixin new behavior to classes you owned and were designing. But you could not mixin anything to types you didn&rsquo;t own. Runtime mixins propose to fill that gap by letting you add a mixin on any type at runtime. If we think again about our example of vehicles with some mixed-in capabilities, if we didn&rsquo;t <em>own</em> James Bond&rsquo;s vehicle and give it some diving ability, we could use this mechanism:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// provided by a third-party
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">interface</span> <span style="color:#0e84b5;font-weight:bold">Vehicle</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    String <span style="color:#06287e">getName</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// provided by a third-party
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">JamesBondVehicle</span> <span style="color:#007020;font-weight:bold">implements</span> Vehicle <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    String <span style="color:#06287e">getName</span><span style="color:#666">()</span> <span style="color:#666">{</span> <span style="color:#4070a0">&#34;James Bond&#39;s vehicle&#34;</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>JamesBondVehicle<span style="color:#666">.</span><span style="color:#4070a0">mixin</span> DivingAbility<span style="color:#666">,</span> FlyingAbility
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">JamesBondVehicle</span><span style="color:#666">().</span><span style="color:#4070a0">fly</span><span style="color:#666">()</span> <span style="color:#666">==</span>
</span></span><span style="display:flex;"><span>       <span style="color:#4070a0">&#34;I&#39;m the James Bond&#39;s vehicle and I fly!&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">JamesBondVehicle</span><span style="color:#666">().</span><span style="color:#4070a0">dive</span><span style="color:#666">()</span> <span style="color:#666">==</span>
</span></span><span style="display:flex;"><span>       <span style="color:#4070a0">&#34;I&#39;m the James Bond&#39;s vehicle and I dive!&#34;</span>
</span></span></code></pre></div><p>One or more mixins can be passed as argument to the static <code>mixin()</code> method added by Groovy on <code>Class</code>.</p>
<h2 id="jsr-223-groovy-scripting-engine">JSR-223 Groovy Scripting Engine</h2>
<p>Before Groovy 1.6, if you wanted to integrate Groovy in your Java projects through JSR-223 / <code>javax.script.*</code>, you had to download a Groovy script engine implementation from java.net, and put the JAR in your classpath. This additional step wasn&rsquo;t very developer friendly, requiring some additional work &mdash; the JAR wasn&rsquo;t even provided in the Groovy distribution. Thankfully, 1.6 comes with an implementation of the <code>javax.script.*</code> APIs.</p>
<p>Below, you&rsquo;ll find an example evaluating Groovy expressions (the code is in Groovy, but it&rsquo;s straightforward to convert it back to Java):</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">javax.script.*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> manager <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> ScriptEngineManager<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> engine <span style="color:#666">=</span> manager<span style="color:#666">.</span><span style="color:#4070a0">getEngineByName</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;groovy&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> engine<span style="color:#666">.</span><span style="color:#4070a0">evaluate</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;2 + 3&#34;</span><span style="color:#666">)</span> <span style="color:#666">==</span> <span style="color:#40a070">5</span>
</span></span></code></pre></div><p>Please note that the <code>javax.script.*</code> APIs are available only on Java 6.</p>
<h2 id="jmx-builder">JMX Builder</h2>
<p>Originiating as an <a href="http://code.google.com/p/groovy-jmx-builder/">external Open-Source project</a> hosted on Google Code, JMX Builder has been integrated in Groovy 1.6, to simplify the life of developers needing to interact or expose JMX services. JMX Builder features:</p>
<ul>
<li>Domain Specific Language (DSL) for JMX API using Builder pattern</li>
<li>Simplified JMX API&rsquo;s programmability</li>
<li>Declaratively expose Java/Groovy objects as JMX managed MBeans</li>
<li>Support class-embedded or explicit descriptors</li>
<li>Inherent support for JMX&rsquo;s event model</li>
<li>Seamlessly create JMX event broadcasters</li>
<li>Attach event listeners as inline closures</li>
<li>Use Groovy&rsquo;s dynamic nature to easily react to JMX events notifications</li>
<li>Provides a flexible registration policy for MBean</li>
<li>No special interfaces or class path restrictions</li>
<li>Shields developer from complexity of JMX API</li>
<li>Exposes attribute, constructors, operations, parameters, and notifications</li>
<li>Simplifies the creation of connector servers and connector clients</li>
<li>Support for exporting JMX timers</li>
</ul>
<p>You can find <a href="http://groovy.codehaus.org/Groovy+JmxBuilder">more information on JMX Builder</a> and its very extensive coverage of the JMX system. Lots of examples will show you how to create a JMX connector server or client, how to easily export POGOs as JMX managed beans, how to listen to JMX events, and much more.</p>
<h2 id="improved-osgi-support">Improved OSGi support</h2>
<p>The Groovy jar files are released with correct OSGi metadata, so they can be loaded as a bundle into any OSGi compliant container, such as Eclipse Equinox or Apache Felix. You can find <a href="http://docs.codehaus.org/display/GROOVY/OSGi+and+Groovy">more information on how to use Groovy and OSGi</a> on the Groovy project website. This tutorial will explain how to:</p>
<ul>
<li>Load Groovy as an OSGi service</li>
<li>Write a Groovy OSGi service</li>
<li>Incude the Groovy JAR within a bundle</li>
<li>Plublish a service written in Groovy</li>
<li>Consume a service from Groovy</li>
<li>Troubleshoot in case you&rsquo;re encountering any problem along the way</li>
</ul>
<p>You may also be interested in, for instance, how you can <a href="http://hamletdarcy.blogspot.com/2008/12/beginners-guide-to-osgi-on-desktop.html">use different versions of Groovy in your application</a>, thanks to OSGi.</p>
<h2 id="summary">Summary</h2>
<p>Groovy continues its march towards the goal of <strong>simplifying the life of developers</strong>, providing various new features and improvements in this new release: AST transformations reducing dramatically the number of lines of code to express certain concerns and patterns and opening the language to developers for further extension, several <strong>metaprogramming enhancements to streamline your code</strong> and let you write <strong>more expressive business rules</strong>, and <strong>support for common enterprise APIs</strong> such as Java 6&rsquo;s scripting APIs, JMX management system, or OSGi&rsquo;s programming model. All of this is done obviously <strong>without compromising on the seamless integration with Java</strong>, and furthermore, with a <strong>level of performance way higher than previous releases</strong>.</p>
<p>We&rsquo;ve now reached the end of this article and if you&rsquo;re not a Groovy user yet, I hope this artcile will give you a better understanding of what Groovy has to offer in your projects, and if you knew and used Groovy already, that you learned about all the new features of the language. The next step for you, dear reader, is to go <a href="http://groovy.codehaus.org/Download">download Groovy 1.6</a>. And if you wish to dive deeper into <a href="http://groovy.codehaus.org/">Groovy</a>, <a href="http://grails.org/">Grails</a> and <a href="http://griffon.codehaus.org/">Griffon</a>, I also invite you to join us at the <a href="http://www.gr8conf.org/">GR8 Conference</a>, a <strong>conference dedicated to Groovy, Grails and Griffon</strong>, taking place in Copenhagen, Denmark, where experts and makers of these technologies will guide you through with practical presentations and hands-on labs.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>The final version of Groovy 1.6 is there</title><link>https://glaforge.dev/posts/2009/02/18/the-final-version-of-groovy-1-6-is-there/</link><pubDate>Wed, 18 Feb 2009 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2009/02/18/the-final-version-of-groovy-1-6-is-there/</guid><description>&lt;p>This is with very great pleasure and honor that I&amp;rsquo;m announcing the &lt;strong>final release of Groovy 1.6&lt;/strong>, on behalf of the Groovy development team and SpringSource.&lt;br />
Obviously, 1.6 is a very important milestone for the project, with several great new features and improvements:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>great runtime performance improvements&lt;/strong>&lt;/li>
&lt;li>&lt;strong>multiple assignments&lt;/strong>&lt;/li>
&lt;li>optional return in &lt;code>if&lt;/code>/&lt;code>else&lt;/code> and &lt;code>try&lt;/code>/&lt;code>catch&lt;/code> blocks&lt;/li>
&lt;li>&lt;strong>AST transformations&lt;/strong> and all the provided transformation annotations like &lt;code>@Singleton&lt;/code>, &lt;code>@Lazy&lt;/code>, &lt;code>@Immutable&lt;/code>, &lt;code>@Delegate&lt;/code> and friends&lt;/li>
&lt;li>the &lt;strong>Grape module and dependency system&lt;/strong> and its &lt;code>@Grab&lt;/code> transformation&lt;/li>
&lt;li>various Swing builder improvements, thanks to the Swing / Griffon (&lt;a href="http://griffon.codehaus.org/">http://griffon.codehaus.org&lt;/a>) team&lt;/li>
&lt;li>as well as several Swing console improvements&lt;/li>
&lt;li>the integration of &lt;strong>JMX builder&lt;/strong>&lt;/li>
&lt;li>&lt;strong>JSR-223 scripting engine built-in&lt;/strong>&lt;/li>
&lt;li>various &lt;strong>metaprogramming improvements&lt;/strong>, like the EMC DSL, per-instance metaclasses even for POJOs, and runtime mixins&lt;/li>
&lt;/ul>
&lt;p>An article will be published in the coming days on InfoQ and &lt;a href="http://www.groovymag.com/">GroovyMag&lt;/a> coming back on all those features and novelties with more details.&lt;/p></description><content:encoded>
<![CDATA[<p>This is with very great pleasure and honor that I&rsquo;m announcing the <strong>final release of Groovy 1.6</strong>, on behalf of the Groovy development team and SpringSource.<br />
Obviously, 1.6 is a very important milestone for the project, with several great new features and improvements:</p>
<ul>
<li><strong>great runtime performance improvements</strong></li>
<li><strong>multiple assignments</strong></li>
<li>optional return in <code>if</code>/<code>else</code> and <code>try</code>/<code>catch</code> blocks</li>
<li><strong>AST transformations</strong> and all the provided transformation annotations like <code>@Singleton</code>, <code>@Lazy</code>, <code>@Immutable</code>, <code>@Delegate</code> and friends</li>
<li>the <strong>Grape module and dependency system</strong> and its <code>@Grab</code> transformation</li>
<li>various Swing builder improvements, thanks to the Swing / Griffon (<a href="http://griffon.codehaus.org/">http://griffon.codehaus.org</a>) team</li>
<li>as well as several Swing console improvements</li>
<li>the integration of <strong>JMX builder</strong></li>
<li><strong>JSR-223 scripting engine built-in</strong></li>
<li>various <strong>metaprogramming improvements</strong>, like the EMC DSL, per-instance metaclasses even for POJOs, and runtime mixins</li>
</ul>
<p>An article will be published in the coming days on InfoQ and <a href="http://www.groovymag.com/">GroovyMag</a> coming back on all those features and novelties with more details.</p>
<p>You can <strong>download Groovy 1.6</strong> at the usual place:<a href="http://groovy.codehaus.org/Download">http://groovy.codehaus.org/Download</a><br />
Additional installers and bundles will be provided soon.</p>
<p>And you can also have a look at the <a href="http://jira.codehaus.org/browse/GROOVY?report=com.atlassian.jira.plugin.system.project:changelog-panel">JIRA changelog</a> listing features, improvements and bug fixes.</p>
<p>Very big thanks to all the developers who contributed to the project, in particular our most prolific ones like Paul, Roshan, Danno and Jochen, and to all our users who&rsquo;ve helped shape the project, thanks to their great feedback and suggestions.</p>
<p>Let me also mention an interesting event which is shaping up: the <strong>GR8 Conference</strong>, a conference dedicated to Groovy, Grails and Griffon, organized by the Danish JUG and SpringSource!</p>
<p>You can learn more about the event on the conference website: <a href="http://www.gr8conf.org/">http://www.gr8conf.org</a> or by following the twitter account: <a href="http://twitter.com/gr8conf">GR8 Conference Twitter account</a></p>
<p>The conference takes place in Copenhagen, Denmark, and will provide<strong>several practical sessions and hands-on labs</strong> <strong>to get you up-to-speed on those great Groovy-based technologies</strong>. The registration should be open in the coming days, so get ready!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 1.6-RC-2 is out! Final version fast approaching!</title><link>https://glaforge.dev/posts/2009/01/22/groovy-1-6-rc-2-is-out-final-version-fast-approaching/</link><pubDate>Thu, 22 Jan 2009 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2009/01/22/groovy-1-6-rc-2-is-out-final-version-fast-approaching/</guid><description>&lt;p>The Groovy development team and SpringSource are pleased to announce the &lt;strong>second candidate for Groovy 1.6&lt;/strong>.&lt;br />
This release is a bug fix release, and as you can see by looking at the &lt;a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;styleName=Html&amp;amp;version=13832">JIRA issues closed&lt;/a> (almost a hundred), a lot of work has been done to ensure that our next major release is of great quality, and various improvements have been introduced &amp;ndash; check for instance the much nicer and thourough &lt;a href="http://groovy.codehaus.org/gapi/">output of GroovyDoc&lt;/a>!&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy development team and SpringSource are pleased to announce the <strong>second candidate for Groovy 1.6</strong>.<br />
This release is a bug fix release, and as you can see by looking at the <a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;styleName=Html&amp;version=13832">JIRA issues closed</a> (almost a hundred), a lot of work has been done to ensure that our next major release is of great quality, and various improvements have been introduced &ndash; check for instance the much nicer and thourough <a href="http://groovy.codehaus.org/gapi/">output of GroovyDoc</a>!</p>
<p>This version obviously also contain all the performance improvements and features of the latest betas and RC. You can read more about them by reading the previous announcements:</p>
<ul>
<li><a href="http://docs.codehaus.org/display/GROOVY/2008/05/02/Groovy+1.6-beta-1+release+with+great+performance+improvements">Groovy 1.6-beta-1</a></li>
<li><a href="http://docs.codehaus.org/display/GROOVY/2008/10/09/Release+of+both+Groovy+1.5.7+and+Groovy+1.6-beta-2">Groovy 1.6-beta-2</a></li>
<li><a href="http://docs.codehaus.org/display/GROOVY/2008/12/22/Release+Candidate+for+Groovy+1.6+available">Groovy 1.6-RC-1</a></li>
</ul>
<p>You can download Groovy 1.6-RC-2 at the usual place: <a href="http://groovy.codehaus.org/Download">http://groovy.codehaus.org/Download</a></p>
<p>And now, <strong>we need your help to test drive this release candidate</strong>! We need you to find possible critical bugs or regressions, so please make sure you try 1.6-RC-2 with your project, to be able to upgrade as soon as possible to the latest and greatest Groovy.</p>
<p>Thank you all for your hard work and your contributions. We&rsquo;re looking forward to your feedback.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>The Groovy 1.6 release candidate is out!</title><link>https://glaforge.dev/posts/2008/12/22/the-groovy-1-6-release-candidate-is-out/</link><pubDate>Mon, 22 Dec 2008 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2008/12/22/the-groovy-1-6-release-candidate-is-out/</guid><description>&lt;p>The Groovy development team and SpringSource are happy to announce the release of the first release candidate of Groovy 1.6.&lt;/p>
&lt;p>The JIRA report for this new version lists 74 bug tickets, 26 improvements and 8 new features:&lt;br />
&lt;a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;styleName=Html&amp;amp;version=14009">http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;styleName=Html&amp;amp;version=14009&lt;/a>&lt;/p>
&lt;p>Among the bugs being fixed, we tackled issues about:&lt;/p>
&lt;ul>
&lt;li>the compiler&lt;/li>
&lt;li>bytecode errors&lt;/li>
&lt;li>varargs handling&lt;/li>
&lt;li>covariant returns&lt;/li>
&lt;li>Windows startup scripts&lt;/li>
&lt;/ul>
&lt;p>As well as a few regressions:&lt;/p>
&lt;ul>
&lt;li>the &lt;code>args&lt;/code> variable not bound in Groovy scripts&lt;/li>
&lt;li>a performance regression in &lt;code>MarkupBuilder&lt;/code>&lt;/li>
&lt;li>a problem with &lt;code>DOMCategory&lt;/code> which was particularly problematic for Grails&lt;/li>
&lt;/ul>
&lt;p>Fixes in line error reporting should be handy for IDE vendors, as well as for Cobertura code coverage.&lt;br />
Compatibility with Java has also been improved slightly, for instance the empty for(;;) {} loop wasn&amp;rsquo;t behaving the same as in Java (no loop, instead of an infinite loop).&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy development team and SpringSource are happy to announce the release of the first release candidate of Groovy 1.6.</p>
<p>The JIRA report for this new version lists 74 bug tickets, 26 improvements and 8 new features:<br />
<a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;styleName=Html&amp;version=14009">http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;styleName=Html&amp;version=14009</a></p>
<p>Among the bugs being fixed, we tackled issues about:</p>
<ul>
<li>the compiler</li>
<li>bytecode errors</li>
<li>varargs handling</li>
<li>covariant returns</li>
<li>Windows startup scripts</li>
</ul>
<p>As well as a few regressions:</p>
<ul>
<li>the <code>args</code> variable not bound in Groovy scripts</li>
<li>a performance regression in <code>MarkupBuilder</code></li>
<li>a problem with <code>DOMCategory</code> which was particularly problematic for Grails</li>
</ul>
<p>Fixes in line error reporting should be handy for IDE vendors, as well as for Cobertura code coverage.<br />
Compatibility with Java has also been improved slightly, for instance the empty for(;;) {} loop wasn&rsquo;t behaving the same as in Java (no loop, instead of an infinite loop).</p>
<p>The XML support continues to be enhanced, for instance with:</p>
<ul>
<li>better handling of namespaces with <code>XmlNodePrinter</code> and <code>NamespaceBuilderSupport</code></li>
<li>new GDK methods in <code>DOMCategory</code></li>
<li>you can customize the nodes in <code>XmlParser</code></li>
<li>a new builder for StAX, thanks to an external contribution</li>
</ul>
<p>In other improvements:</p>
<ul>
<li>Groovysh can be extended, giving you access to certain key internal structures</li>
<li>the stacktrace sanitization can be customized should you want to print out nicer stacktraces.</li>
<li>new GDK methods have been added for File handling and Date formatting</li>
<li>a new <code>@PackageScope</code> AST transformation was added to support the package-scope visibility Groovy didn&rsquo;t support</li>
<li>improvements to our OSGi manifest for better OSGi support</li>
<li>the default resolve strategy used for <code>.with{}</code> method has been changed to <code>DELEGATE_FIRST</code></li>
<li>GroovyDoc now supports multiple locations in sourcepath</li>
<li>an @since tag should be used in documenting the GDK methods so users know when a given GDK method has been added to Groovy</li>
</ul>
<p>Beyond continuous improvements in Groovy&rsquo;s Swing support thanks to our <a href="http://griffon.codehaus.org/">Griffon</a> Swing team, the Swing console has seen some interesting improvements as well:</p>
<ul>
<li>when you have a stacktrace showing up because of an exception being thrown in your scripts, or when you have a compiler error, you&rsquo;ll get clickable messages in the output window, making it easier to navigate to the offending line of code causing the problem</li>
<li>you can also now opt to hide the script recopy in the output window</li>
<li>and you can also have a visual representation of output results, for instance with Swing components not attached to any parent being displayed on the output, and the nice thing to consider is also the fact anybody is able to customize the output visualizations.</li>
</ul>
<p>Last but not least, Vladimir Vivien&rsquo;s <a href="http://www.infoq.com/news/2008/12/jmx_builder">JmxBuilder</a> originally hosted at <a href="http://code.google.com/p/groovy-jmx-builder/">Google Code</a> was contributed back to Groovy, further improving our existing support of JMX.</p>
<p>Beyond all these bug fixes and other improvements, you still have all the novelties and improvements listed in the betas:</p>
<ul>
<li><a href="http://glaforge.free.fr/weblog/index.php?itemid=241">release notes for 1.6-beta-1</a></li>
<li><a href="http://glaforge.free.fr/weblog/index.php?itemid=256">release notes for 1.6-beta-2</a></li>
</ul>
<p>Please make sure you try this release candidate within your projects without waiting for the final release, so that we can ensure the final version works well for you.</p>
<p>As usual, you can download Groovy here: <a href="http://groovy.codehaus.org/Download">http://groovy.codehaus.org/Download</a><br />
Noteworthy as well is that we have &ldquo;retrotranslated&rdquo; Groovy 1.6-RC-1 to JDK 1.4, should you have to stay with a JDK 1.4 for running Groovy:<a href="http://repository.codehaus.org/org/codehaus/groovy/">http://repository.codehaus.org/org/codehaus/groovy/</a></p>
<p>Thanks a lot to all the contributors and committers who made this release possible!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>SpringSource acquires G2One</title><link>https://glaforge.dev/posts/2008/11/11/springsource-acquires-g2one/</link><pubDate>Tue, 11 Nov 2008 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2008/11/11/springsource-acquires-g2one/</guid><description>&lt;p>&lt;figure>
&lt;a href="#img-4ce99f3fd3c519299dd8224c0f3dbbc8">
&lt;img src="http://www.springsource.com/files/u5/SpringSource-Acquires.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-4ce99f3fd3c519299dd8224c0f3dbbc8">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="http://www.springsource.com/files/u5/SpringSource-Acquires.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
I&amp;rsquo;m very pleased to echo, here on my blog, the &lt;a href="http://www.springsource.com/g2one">announcement of the acquisition of G2One, the Groovy/Grails company I co-founded, by SpringSource&lt;/a>, the company behind the Spring framework!&lt;/p>
&lt;p>Everybody knows &lt;a href="http://www.springframework.org/">Spring&lt;/a> and &lt;a href="http://www.springsource.com/">SpringSource&lt;/a> already, its wealth of Enterprise projects, and how it quickly became the de facto Enterprise standard for building mission-critical applications. Both Groovy and Grails will bring more dynamicity and agility to the Spring portofolio projects, thanks to tighter integration, cross-polination, and further extensibility. At the same time, the two G&amp;rsquo;s will most probably bring a fun coolness factor into the mix, like the icing on the cake!&lt;/p></description><content:encoded>
<![CDATA[<p><figure>
  <a href="#img-4ce99f3fd3c519299dd8224c0f3dbbc8">
    <img src="http://www.springsource.com/files/u5/SpringSource-Acquires.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-4ce99f3fd3c519299dd8224c0f3dbbc8">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="http://www.springsource.com/files/u5/SpringSource-Acquires.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
I&rsquo;m very pleased to echo, here on my blog, the <a href="http://www.springsource.com/g2one">announcement of the acquisition of G2One, the Groovy/Grails company I co-founded, by SpringSource</a>, the company behind the Spring framework!</p>
<p>Everybody knows <a href="http://www.springframework.org/">Spring</a> and <a href="http://www.springsource.com/">SpringSource</a> already, its wealth of Enterprise projects, and how it quickly became the de facto Enterprise standard for building mission-critical applications. Both Groovy and Grails will bring more dynamicity and agility to the Spring portofolio projects, thanks to tighter integration, cross-polination, and further extensibility. At the same time, the two G&rsquo;s will most probably bring a fun coolness factor into the mix, like the icing on the cake!</p>
<p>And you can certainly imagine some areas where integrating Groovy in existing Spring powered projects may be advantageous. Think for a minute about what we could achieve with Groovy integration in the Spring portfolio: think of scripted deployments on dm Server? Writing Spring batch jobs with Groovy scripts, that even administrator could develop and maintain? Easily manipulating Spring exposed JMX beans with Groovy&rsquo;s JMX support? Introspecting a deployed live application with a remote Groovy shell or sending script probes? I&rsquo;m sure we&rsquo;ll find tons of ways to gain from such an integration.</p>
<p>Also, speaking about integration, let me also mention my pet topic of mine: Domain-Specific Languages. You know it&rsquo;s been a while since Spring supported seamlessly integrating and mixing Groovy and Java wired beans together. But with the high flexibility of Groovy, its malleable syntax and powerful meta-programming APIs, Groovy on Spring will unleash the power of Domain-Specific Languages in the Enterprise world! Subject matter experts can already take advantage of Groovy to write business rules in Groovy for their mission critical applications: but here, Spring would become the fabric tying this business logic to Enterprise services, and Groovy the language for clearly expressing business concerns.</p>
<p>Now, more concretely, for Groovy, what does it mean? I see several key benefits to this acquisition.</p>
<p>First of all, SpringSource&rsquo;s Eclipse team will join forces with our own Eclipse team to shift gears in the development of the Groovy/Grails Eclipse plugin. The lack of state of the art Groovy support in Eclipse can still be a limiting factor to adoption in the wild. However improved the plugin has gotten over the past year, lots of work is still needed to bring it on-par with expectations people have when working with their usual Java IDEs. So hopefully, in the coming months, you should see improved support of both Groovy and Grails in Eclipse.</p>
<p>Secondly, SpringSource has extensive experience in managing community-led projects like Tomcat, so their input will be beneficial to the way we run the Groovy project. Also, don&rsquo;t worry, Groovy will not change its license (Groovy is licensed under the ASL 2 license), or anything like that: SpringSource is committed to sustaining and helping the development of the Groovy dynamic language, to bring it to new heights!</p>
<p>Thirdly, I think Groovy will also benefit from Spring&rsquo;s visibility in the Enterprise space and Java ecosystem by bringing added visibility and better and wider marketing. Although Groovy is already the most popular alternative language for the JVM, additional help is always good to make Groovy even more popular!</p>
<p>All in all, I&rsquo;m very happy to join SpringSource, to continue developing Groovy under their umbrella and with their help and support, and I&rsquo;m sure everybody will benefit from this closer relationship between our teams. I also encourage you to read <a href="http://graemerocher.blogspot.com/2008/11/groovy-and-grails-join-spring-family.html">Graeme&rsquo;s post</a> which goes deeper in the benefits for the Grails community as well and read the <a href="http://www.springsource.com/files/G2OnePublicFAQ.pdf">FAQ regarding the acquisition</a>, as well as the <a href="http://www.springsource.com/node/836">press release</a>. Both Graeme and I, as well as the rest of the <a href="http://www.g2one.com/">G2One</a> team, will remain committed to the projects, and will continue to develop and improve them in the coming years.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Sun abandons Swing!</title><link>https://glaforge.dev/posts/2008/11/06/sun-abandons-swing/</link><pubDate>Thu, 06 Nov 2008 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2008/11/06/sun-abandons-swing/</guid><description>&lt;p>I&amp;rsquo;m really sad to hear the &lt;a href="http://www.pushing-pixels.org/?p=758">report Kirill makes&lt;/a> on &lt;strong>Sun progressively abandonning Swing&lt;/strong> (also posted on &lt;a href="http://weblogs.java.net/blog/kirillcool/archive/2008/11/sun_setting_dow.html">java.net&lt;/a>).&lt;/p>
&lt;p>Swing is really a very good framework for building rich client applications, and from what I&amp;rsquo;ve heard and seen, it&amp;rsquo;s even better than what exists in the .Net world, or compared to things like SWT or Cocoa. Sun is leaving a gem in the cold to bet everything on a half-backed JavaFX technology.&lt;/p>
&lt;p>With the focus on JavaFX, Sun progressively lost all its key talented employees who preferred sailing to more gorgeous seas &amp;ndash; I can&amp;rsquo;t blame them for that. With the new app framework, the timing framework, SwingLabs, painters, new look&amp;rsquo;n feels, a wealth of OSS and commercial components, Swing had great chances to keep up with the rest of the world, and even keep its bleeding edge and stay ahead of the curve. Alas, Swing is dying in favor of a new technology nobody cares about &amp;ndash; why would one use JavaFX when Adobe&amp;rsquo;s Flex and Microsoft&amp;rsquo;s Silverlight are so much more advanced and ready for prime-time, thought and tool&amp;rsquo;ed for the designer in mind?&lt;/p></description><content:encoded>
<![CDATA[<p>I&rsquo;m really sad to hear the <a href="http://www.pushing-pixels.org/?p=758">report Kirill makes</a> on <strong>Sun progressively abandonning Swing</strong> (also posted on <a href="http://weblogs.java.net/blog/kirillcool/archive/2008/11/sun_setting_dow.html">java.net</a>).</p>
<p>Swing is really a very good framework for building rich client applications, and from what I&rsquo;ve heard and seen, it&rsquo;s even better than what exists in the .Net world, or compared to things like SWT or Cocoa. Sun is leaving a gem in the cold to bet everything on a half-backed JavaFX technology.</p>
<p>With the focus on JavaFX, Sun progressively lost all its key talented employees who preferred sailing to more gorgeous seas &ndash; I can&rsquo;t blame them for that. With the new app framework, the timing framework, SwingLabs, painters, new look&rsquo;n feels, a wealth of OSS and commercial components, Swing had great chances to keep up with the rest of the world, and even keep its bleeding edge and stay ahead of the curve. Alas, Swing is dying in favor of a new technology nobody cares about &ndash; why would one use JavaFX when Adobe&rsquo;s Flex and Microsoft&rsquo;s Silverlight are so much more advanced and ready for prime-time, thought and tool&rsquo;ed for the designer in mind?</p>
<p>There are days where I really don&rsquo;t understand Sun and its decisions. Why abandonning such a great technology? I know it&rsquo;s not always the best technologies that win and prevail, but when you&rsquo;ve got a competitive advantage in one area, why not keeping it? Instead of betting everything on the new shiny toy of the day? A shiny toy that&rsquo;s been shamelessly crashing all the JavaOne keynote demos?</p>
<p>I&rsquo;m perplexed!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>GroovyMag, the Groovy / Grails magazine is out!</title><link>https://glaforge.dev/posts/2008/11/04/groovymag-the-groovy-grails-magazine-is-out/</link><pubDate>Tue, 04 Nov 2008 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2008/11/04/groovymag-the-groovy-grails-magazine-is-out/</guid><description>&lt;p>&lt;figure>
&lt;a href="#img-d98f2e1d33759828fc50a840d633a971">
&lt;img src="http://www.groovymag.com/images/gm1_200.jpg"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-d98f2e1d33759828fc50a840d633a971">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="http://www.groovymag.com/images/gm1_200.jpg"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
I&amp;rsquo;m very happy to echo the release of the first issue of &lt;a href="http://www.groovymag.com/main/">GroovyMag&lt;/a>, the Groovy and Grails magazine! This is an electronic PDF magazine which will bring you news, articles and tutorials around the &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> dynamic language for the JVM and the &lt;a href="http://grails.org/">Grails&lt;/a> agile web application framework.&lt;/p>
&lt;p>As the &lt;a href="http://www.groovymag.com/main/">site&lt;/a> shows:&lt;/p>
&lt;blockquote>
&lt;p>GroovyMag covers a wide variety of topics in the Groovy and Grails world, featuring some of the best and brightest names in the Groovosphere. Our first issue includes a Grails tutorial, a Groovy/Swing tutorial, community news and more.&lt;/p></description><content:encoded>
<![CDATA[<p><figure>
  <a href="#img-d98f2e1d33759828fc50a840d633a971">
    <img src="http://www.groovymag.com/images/gm1_200.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-d98f2e1d33759828fc50a840d633a971">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="http://www.groovymag.com/images/gm1_200.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
I&rsquo;m very happy to echo the release of the first issue of <a href="http://www.groovymag.com/main/">GroovyMag</a>, the Groovy and Grails magazine! This is an electronic PDF magazine which will bring you news, articles and tutorials around the <a href="http://groovy.codehaus.org/">Groovy</a> dynamic language for the JVM and the <a href="http://grails.org/">Grails</a> agile web application framework.</p>
<p>As the <a href="http://www.groovymag.com/main/">site</a> shows:</p>
<blockquote>
<p>GroovyMag covers a wide variety of topics in the Groovy and Grails world, featuring some of the best and brightest names in the Groovosphere. Our first issue includes a Grails tutorial, a Groovy/Swing tutorial, community news and more.</p></blockquote>
<p>In this first issue, you&rsquo;ll find:</p>
<ul>
<li>A preface by Michael Kimsal, the creator of GroovyMag</li>
<li>Groovy and Grails community news, and a featured Grails plugin, by Dave Klein</li>
<li>A Grails introduction article, by Robert Fischer</li>
<li>Builder Swing rich applications with Groovy, by Andres Almiray</li>
<li>An opinion piece on Groovy being the future of Java, by Shawn Hartsock</li>
<li>An interview of Marcus Irven of Minggl, a company bulding products in Grails</li>
</ul>
<p>GroovyMag looks very promising, and I wish the best of luck to Michael and its editors, authors and columnists. I also look forward to finding some time to contribute to the magazine myself! <a href="http://www.groovymag.com/main/">Go check it out</a>, it&rsquo;s worth every cent!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Both Groovy 1.5.7 and 1.6-beta-2 are out!</title><link>https://glaforge.dev/posts/2008/10/09/both-groovy-1-5-7-and-1-6-beta-2-are-out/</link><pubDate>Thu, 09 Oct 2008 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2008/10/09/both-groovy-1-5-7-and-1-6-beta-2-are-out/</guid><description>&lt;p>This is with great pleasure that the Groovy development team and &lt;a href="http://www.g2one.com/">G2One&lt;/a> announce the &lt;strong>joint release of both Groovy 1.5.7&lt;/strong> &amp;ndash; current stable and maintenance branch &amp;ndash; and &lt;strong>Groovy 1.6-beta-2&lt;/strong> &amp;ndash; the upcoming major release.&lt;/p>
&lt;p>Groovy 1.5.7 contains mainly bug fixes (61 bug fixes), but also some minor API improvements (20 improvements) backported from the1.6 branch, whereas Groovy 1.6-beta-2 brings a wealth of novelty (68 bug fixes and 38 improvements and new features). Here, we&amp;rsquo;ll mainly cover the new features of beta-2.&lt;/p></description><content:encoded>
<![CDATA[<p>This is with great pleasure that the Groovy development team and <a href="http://www.g2one.com/">G2One</a> announce the <strong>joint release of both Groovy 1.5.7</strong> &ndash; current stable and maintenance branch &ndash; and <strong>Groovy 1.6-beta-2</strong> &ndash; the upcoming major release.</p>
<p>Groovy 1.5.7 contains mainly bug fixes (61 bug fixes), but also some minor API improvements (20 improvements) backported from the1.6 branch, whereas Groovy 1.6-beta-2 brings a wealth of novelty (68 bug fixes and 38 improvements and new features). Here, we&rsquo;ll mainly cover the new features of beta-2.</p>
<p>Following up on our major compile-time and runtime performance improvements in beta-1, we continued our efforts in that area for beta-2. But you&rsquo;ll also be interested in discovering the changes and new features brought by this new release.</p>
<h2 id="multiple-assignments">Multiple Assignments</h2>
<p>In beta-1, we introduced the possibility to define multiple assignments. However, we faced some issues and ambiguities with the subscript notation, so we adapted the syntax slightly by using parentheses instead of square brackets. We also now cover all the assignment use cases, which wasn&rsquo;t the case previously.</p>
<p>You can define and assign several variables at once:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> <span style="color:#666">(</span>a<span style="color:#666">,</span> b<span style="color:#666">)</span> <span style="color:#666">=</span> <span style="color:#666">[</span><span style="color:#40a070">1</span><span style="color:#666">,</span><span style="color:#40a070">2</span><span style="color:#666">]</span>  
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> a <span style="color:#666">==</span> <span style="color:#40a070">1</span>  
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> b <span style="color:#666">==</span> <span style="color:#40a070">2</span>  
</span></span></code></pre></div><p>And you can also define the types of the variables in one shot as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> <span style="color:#666">(</span><span style="color:#902000">int</span> i<span style="color:#666">,</span> String j<span style="color:#666">)</span> <span style="color:#666">=</span> <span style="color:#666">[</span><span style="color:#40a070">1</span><span style="color:#666">,</span> <span style="color:#4070a0">&#39;Groovy&#39;</span><span style="color:#666">]</span>  
</span></span></code></pre></div><p>For the assignment (without priori definition of the variables), just don&rsquo;t use the &lsquo;def&rsquo; keyword:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> a<span style="color:#666">,</span> b  
</span></span><span style="display:flex;"><span><span style="color:#666">(</span>a<span style="color:#666">,</span> b<span style="color:#666">)</span> <span style="color:#666">=</span> functionReturningAList<span style="color:#666">()</span>  
</span></span></code></pre></div><p>Otherwise, apart from the syntax change, the behavior is the same as in beta-1: if the list on the right-hand side contains more elements than the number of variables on the left-hand side, only the first elements will be assigned in order into the variables. Also, when there are less elements than variables, the extra variables will be assigned null.</p>
<h2 id="optional-return-for-ifelse-and-trycatchfinally-blocks">Optional return for if/else and try/catch/finally blocks</h2>
<p>Although not a syntax change, but at least a new behavior: it is now possible for <code>if</code>/<code>else</code> and <code>try</code>/<code>catch</code>/<code>finally</code> blocks to return a value. No need to explicitly use the <code>return</code> keyword inside these constructs, as long as they are the latest statement in the block of code.</p>
<p>As an example, the following method will return 1, although the <code>return</code> keyword was omitted.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span> <span style="color:#902000">def</span> <span style="color:#06287e">method</span><span style="color:#666">()</span> <span style="color:#666">{</span>  
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span><span style="color:#007020;font-weight:bold">true</span><span style="color:#666">)</span> <span style="color:#40a070">1</span> <span style="color:#007020;font-weight:bold">else</span> <span style="color:#40a070">0</span>  
</span></span><span style="display:flex;"><span> <span style="color:#666">}</span>  
</span></span></code></pre></div><p>You can have a look at our <a href="http://fisheye.codehaus.org/browse/~raw,r=13627/groovy/trunk/groovy/groovy-core/src/test/groovy/lang/SynteticReturnTest.groovy">test case</a> to have an overview of this new behavior.</p>
<h2 id="ast-transformations">AST Transformations</h2>
<p>Although at times, it may sound like a good idea to extend the syntax of Groovy to implement new features (like this is the case for instance for multiple assignments), most of the time, we can&rsquo;t just add a new keyword to the grammar, or create some new syntax construct to represent a new concept. However, with the idea of AST (Abstract Syntax Tree) Transformations initiated and introduced in beta-1, we are able to tackle new and innovative ideas without necessary grammar changes.</p>
<p>With AST Transformations, people can hook into the compiler to make some changes to the AST in order to change the program that is being compiled. AST Transformations provides Groovy with improved compile-time metaprogramming capabilities allowing powerful flexibility at the language level, without a runtime performance penalty.</p>
<p>In beta-1, two AST Transformations found their way in the release, pioneered by our talented Groovy Swing team. With the @Bindable transformation marker annotation, property listeners are transparently added to the class for the property that is being annotated. Sames goes for the @Vetoable annotation for vetoing property changes.</p>
<p>In beta-2, new transformations have been created:</p>
<ul>
<li><code>@Singleton</code> to transform a class into a singleton (<a href="http://fisheye.codehaus.org/browse/~raw,r=13529/groovy/trunk/groovy/groovy-core/src/test/org/codehaus/groovy/transform/vm5/GlobalTransformTest.groovy">example</a>)</li>
<li><code>@Immutable</code> to forbid changes to an instance once it&rsquo;s been created (<a href="http://fisheye.codehaus.org/browse/~raw,r=13619/groovy/trunk/groovy/groovy-core/src/test/org/codehaus/groovy/transform/vm5/ImmutableTransformTest.groovy">example</a>)</li>
<li><code>@Delegate</code> transparently implement the delegation pattern (<a href="http://fisheye.codehaus.org/browse/~raw,r=13592/groovy/trunk/groovy/groovy-core/src/test/org/codehaus/groovy/transform/vm5/DelegateTransformTest.groovy">example</a>)</li>
<li><code>@Lazy</code> for lazily initializing properties (<a href="http://fisheye.codehaus.org/browse/~raw,r=13545/groovy/trunk/groovy/groovy-core/src/test/org/codehaus/groovy/transform/vm5/LazyTransformTest.groovy">example</a>)</li>
<li><code>@Category</code> / <code>@Mixin</code> helps mixin category methods at compile-time (<a href="http://fisheye.codehaus.org/browse/~raw,r=12588/groovy/trunk/groovy/groovy-core/src/test/groovy/lang/vm5/MixinAnnotationTest.groovy">example</a>)</li>
<li><code>@Newify</code> allows to omit using the <code>new</code> keyword for creating instances, simply doing Integer(5) will create an instance, and also gives you the ability to use a Ruby-esque syntax with <code>Integer.new(5)</code></li>
</ul>
<p>Documentation will come on those transformations in time for the final release. In the meantime, don&rsquo;t hesitate to ask questions on the mailing-lists if you&rsquo;d like to know more.</p>
<h2 id="swing-builder">Swing builder</h2>
<p>The swing builder which pioneered AST transformations added support for binding to simple closures, i.e. <code>bean(property: bind { otherBean.property } )</code> is the same as <code>bind(source: otherBean, target: property)</code>. It is also a closure so you can do more complex expressions like <code>bean(location: bind {pos.x + ',' + pos.y} )</code> &ndash; but the expression needs to stay simple enough: no loops or branching, for instance.</p>
<h2 id="grape-the-adaptable--advanced-packaging-engine">Grape, the adaptable / advanced packaging engine</h2>
<p>Also leveraging AST Transformations, Groovy 1.6-beta-2 provides a preview of the Groovy Advanced or Adaptable Packaging Engine. Groovy scripts can require certain libraries: by explicitly saying so in your script with the @Grab transformation or with the Grape.grab() method call, the runtime will find the needed JARs for you. With Grape, you can easily distribute scripts without their dependencies, and have them downloaded on first use of your script.</p>
<p>For more information on Grape, please read the <a href="http://groovy.codehaus.org/Grape">documentation</a>.</p>
<h2 id="per-instance-metaclass-even-for-pojos">Per instance metaclass even for POJOs</h2>
<p>So far, Groovy classes could have a per-instance metaclass, but POJOs could only have on metaclass for all instances (ie. a per-class metaclass). This is now not the case anymore, as POJOs can have per-instance metaclass too. Also, setting the metaclass property to null will restore the default metaclass.</p>
<h2 id="expandometaclass-dsl">ExpandoMetaClass DSL</h2>
<p>A new DSL for EMC was created to allow chaining the addition of new methods through EMC without the usual object.metaClass repetition. You can find some examples in our test cases again: <a href="http://fisheye.codehaus.org/browse/~raw,r=12759/groovy/trunk/groovy/groovy-core/src/test/org/codehaus/groovy/runtime/PerInstanceMetaClassTest.groovy">here</a> and <a href="http://fisheye.codehaus.org/browse/~raw,r=13016/groovy/trunk/groovy/groovy-core/src/test/groovy/vm5/ActorTest.groovy">there</a>.</p>
<h2 id="runtime-mixins">Runtime mixins</h2>
<p>One last feature I&rsquo;ll mention today is runtime mixins. Unlike the @Category/@Mixin combo, or even the @Delegate transformation, as their name imply, mixin behavior at runtime. You can find examples <a href="http://fisheye.codehaus.org/browse/~raw,r=13119/groovy/trunk/groovy/groovy-core/src/test/groovy/lang/vm5/MixinTest.groovy">here</a>, which also combine the use of the EMC DSL</p>
<h2 id="useful-links">Useful links</h2>
<p>You can <a href="http://groovy.codehaus.org/Download">download both versions here</a>, and the JARs will be available shortly on the central Maven repository.</p>
<p>And you can read the <a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;styleName=Html&amp;version=14242">release notes from JIRA for 1.5.7</a> and the <a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;styleName=Html&amp;version=14261">release notes for 1.6-beta-2</a>.</p>
<h2 id="further-down-the-road">Further down the road</h2>
<p>We adapted the Groovy roadmap a little: as features which were supposed to be in 1.7 have already been implemented in 1.6, the scope of 1.7 is reduced and 1.8 is made useless, so after 1.6, we&rsquo;ll focus on 1.7 which will mainly bring one new major feature (the ability to define anonymous inner classes in Groovy, to bridge the small difference gap with Java), and afterwards, we&rsquo;ll be able to focus our energy on Groovy 2.0 improving and rewriting parts of the Groovy core to bring in even more performance and to pave the way for even more power features in future versions.</p>
<p>We&rsquo;re looking forward to hearing about your feedback on those two releases.<br />
Thanks a lot to everybody for their help and respective contributions.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Listing the properties of a class in order</title><link>https://glaforge.dev/posts/2008/10/08/listing-the-properties-of-a-class-in-order/</link><pubDate>Wed, 08 Oct 2008 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2008/10/08/listing-the-properties-of-a-class-in-order/</guid><description>&lt;p>In the same vein as my recent article on &lt;a href="http://glaforge.free.fr/weblog/index.php?itemid=247">how to know the variables which are bound or not in a script&lt;/a>, a user asked &lt;a href="http://markmail.org/message/pmg6h5wfpclwqemd">how he could list in order the properties defined in a class&lt;/a>. Unfortunately, using MyClass.metaClass.properties won&amp;rsquo;t guarantee the order in which properties were created. But reusing the technique in my previous article, one can visit the Groovy AST, in order, and just look at the property definitions, fill an ordered list of these properties, and return it once the traversal is finished. Here&amp;rsquo;s what I came up with:&lt;/p></description><content:encoded>
<![CDATA[<p>In the same vein as my recent article on <a href="http://glaforge.free.fr/weblog/index.php?itemid=247">how to know the variables which are bound or not in a script</a>, a user asked <a href="http://markmail.org/message/pmg6h5wfpclwqemd">how he could list in order the properties defined in a class</a>. Unfortunately, using MyClass.metaClass.properties won&rsquo;t guarantee the order in which properties were created. But reusing the technique in my previous article, one can visit the Groovy AST, in order, and just look at the property definitions, fill an ordered list of these properties, and return it once the traversal is finished. Here&rsquo;s what I came up with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.codehaus.groovy.ast.expr.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.codehaus.groovy.ast.stmt.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.codehaus.groovy.ast.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.codehaus.groovy.control.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.codehaus.groovy.classgen.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">java.security.CodeSource</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> scriptText <span style="color:#666">=</span> <span style="color:#4070a0">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">class Customer {
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    def name
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    def phone
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    def address1
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">    def address2
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">}
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// we define a custom visitor that traverses the AST just to look at class properties
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">PropertyVisitor</span> <span style="color:#007020;font-weight:bold">extends</span> ClassCodeVisitorSupport <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> orderedProperties <span style="color:#666">=</span> <span style="color:#666">[]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">void</span> <span style="color:#06287e">visitProperty</span><span style="color:#666">(</span>PropertyNode node<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        orderedProperties <span style="color:#666">&lt;&lt;</span> node<span style="color:#666">.</span><span style="color:#4070a0">name</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">protected</span> SourceUnit <span style="color:#06287e">getSourceUnit</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">return</span> source<span style="color:#666">;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// we define our custom PrimaryClassNodeOperation
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// to be able to hook our code visitor
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">CustomSourceOperation</span> <span style="color:#007020;font-weight:bold">extends</span> CompilationUnit<span style="color:#666">.</span><span style="color:#4070a0">PrimaryClassNodeOperation</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    CodeVisitorSupport visitor
</span></span><span style="display:flex;"><span>    <span style="color:#902000">void</span> <span style="color:#06287e">call</span><span style="color:#666">(</span>SourceUnit source<span style="color:#666">,</span> GeneratorContext context<span style="color:#666">,</span> ClassNode classNode<span style="color:#666">)</span> <span style="color:#007020;font-weight:bold">throws</span> CompilationFailedException <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        classNode<span style="color:#666">.</span><span style="color:#4070a0">visitContents</span><span style="color:#666">(</span>visitor<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// we use our own class loader to add our phase operation
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">MyClassLoader</span> <span style="color:#007020;font-weight:bold">extends</span> GroovyClassLoader <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    CodeVisitorSupport visitor
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">protected</span> CompilationUnit <span style="color:#06287e">createCompilationUnit</span><span style="color:#666">(</span>CompilerConfiguration config<span style="color:#666">,</span> CodeSource source<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        CompilationUnit cu <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">super</span><span style="color:#666">.</span><span style="color:#4070a0">createCompilationUnit</span><span style="color:#666">(</span>config<span style="color:#666">,</span> source<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        cu<span style="color:#666">.</span><span style="color:#4070a0">addPhaseOperation</span><span style="color:#666">(</span><span style="color:#007020;font-weight:bold">new</span> CustomSourceOperation<span style="color:#666">(</span><span style="color:#002070;font-weight:bold">visitor:</span> visitor<span style="color:#666">),</span> Phases<span style="color:#666">.</span><span style="color:#4070a0">CLASS_GENERATION</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">return</span> cu
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> visitor <span style="color:#666">=</span>  <span style="color:#007020;font-weight:bold">new</span> PropertyVisitor<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> myCL <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> MyClassLoader<span style="color:#666">(</span><span style="color:#002070;font-weight:bold">visitor:</span> visitor<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// simply by parsing the script with our classloader
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// our visitor will be called and will visit the properties of the class in order
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> script <span style="color:#666">=</span> myCL<span style="color:#666">.</span><span style="color:#4070a0">parseClass</span><span style="color:#666">(</span>scriptText<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#666">[</span><span style="color:#4070a0">&#39;name&#39;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#39;phone&#39;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#39;address1&#39;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#39;address2&#39;</span><span style="color:#666">]</span> <span style="color:#666">==</span> visitor<span style="color:#666">.</span><span style="color:#4070a0">orderedProperties</span>
</span></span></code></pre></div><p>Seeing that such AST analysis can be useful from times to times, I even think we could add some GroovyShell#parseWithVisitor(myscript, myvisitor) to avoid writing the same boiler-plate code of setting up the classloader, compilation unit, and so on. This could be pretty handy. After a discussion with Jochen, we even thought some kind of debugging visitor could be handy, where you&rsquo;d visit the AST interactively, executing some code on certain nodes, and the tool would record everything, so that you can later reuse that visit. This could be a fun little experiment, and a handy tool when someone needs to do some analysis of the code.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy in Action book now in Japanese!</title><link>https://glaforge.dev/posts/2008/09/21/groovy-in-action-book-now-in-japanese/</link><pubDate>Sun, 21 Sep 2008 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2008/09/21/groovy-in-action-book-now-in-japanese/</guid><description>&lt;p>&lt;figure>
&lt;a href="#img-e6a96dfe885bcf65607bc847ff730d5c">
&lt;img src="http://book.mycom.co.jp/MYCOM/image/book/978-4-8399-2727-1.jpg"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-e6a96dfe885bcf65607bc847ff730d5c">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="http://book.mycom.co.jp/MYCOM/image/book/978-4-8399-2727-1.jpg"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
After the &lt;a href="http://www.amazon.de/Groovy-Einsatz-Andrew-Guillaume-Laforge/dp/3446412387">German translation of &lt;/a>&lt;a href="http://groovy.canoo.com/gina">Groovy in Action&lt;/a>, there&amp;rsquo;s now a Japanese version also available! But not only has it been translated, but it has also been improved and covers Groovy 1.5.6 and contains fixes to all the errata of the original version.&lt;/p>
&lt;p>Here&amp;rsquo;s the announcement from Dierk:&lt;/p>
&lt;blockquote>
&lt;p>I&amp;rsquo;m very happy to announce that the japanese edition of &amp;ldquo;Groovy in Action&amp;rdquo; has just hit the shelves.&lt;/p>
&lt;p>Please visit the &lt;a href="http://book.mycom.co.jp/book/978-4-8399-2727-1/978-4-8399-2727-1.shtml">publisher&amp;rsquo;s page&lt;/a> or &lt;a href="http://www.amazon.co.jp/dp/4839927278/">on Amazon Japan&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p><figure>
  <a href="#img-e6a96dfe885bcf65607bc847ff730d5c">
    <img src="http://book.mycom.co.jp/MYCOM/image/book/978-4-8399-2727-1.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-e6a96dfe885bcf65607bc847ff730d5c">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="http://book.mycom.co.jp/MYCOM/image/book/978-4-8399-2727-1.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
After the <a href="http://www.amazon.de/Groovy-Einsatz-Andrew-Guillaume-Laforge/dp/3446412387">German translation of </a><a href="http://groovy.canoo.com/gina">Groovy in Action</a>, there&rsquo;s now a Japanese version also available! But not only has it been translated, but it has also been improved and covers Groovy 1.5.6 and contains fixes to all the errata of the original version.</p>
<p>Here&rsquo;s the announcement from Dierk:</p>
<blockquote>
<p>I&rsquo;m very happy to announce that the japanese edition of &ldquo;Groovy in Action&rdquo; has just hit the shelves.</p>
<p>Please visit the <a href="http://book.mycom.co.jp/book/978-4-8399-2727-1/978-4-8399-2727-1.shtml">publisher&rsquo;s page</a> or <a href="http://www.amazon.co.jp/dp/4839927278/">on Amazon Japan</a>.</p>
<p>Kazuchika Sekiya and his five friends did an awesome job in not only translating the book &ldquo;as is&rdquo; but also updating it to the latest Groovy version (1.5.6), adapting all listings and explanations. Furthermore, they went through all known errata of the original version.</p>
<p>Above all, they did not blindly translate word-by-word but challened the meaning of every sentence, asked for clarification, and came up with improved wordings that avoid any kind of ambiguity.</p>
<p>It should not be left unmentioned that the translators work for Sun Microsystems Japan in their day job but did all that hard work in their spare time.</p>
<p>I&rsquo;m very thankful for the experience of having seen this translation develop and now seeing the grace of the final result. Let&rsquo;s spread the word about Groovy!</p>
<p>Thank you, guys - domo arigato gozaimasu!</p></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>JavaZone: Writing Groovy DSLs presentation online</title><link>https://glaforge.dev/talks/2008/09/17/javazone-writing-groovy-dsls-presentation-online/</link><pubDate>Wed, 17 Sep 2008 00:00:00 +0200</pubDate><guid>https://glaforge.dev/talks/2008/09/17/javazone-writing-groovy-dsls-presentation-online/</guid><description>&lt;p>I&amp;rsquo;ve just uploaded my &lt;a href="http://javazone.no/incogito/session/Writing+Domain-Specific+Languages+with+Groovy.html">JavaZone&lt;/a> slides of the Domain-Specific Language presentation available on Slideshare.&lt;/p>
&lt;script async class="speakerdeck-embed" data-id="cde49b5be5f84f5088048076df87c5a8" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js">&lt;/script>
&lt;blockquote>
&lt;p>&lt;strong>Abstract:&lt;/strong> Domain-Specific Languages are an &amp;ldquo;architectural hot-spot&amp;rdquo;, as coined recently by InfoQ editors. With dynamic languages such as Groovy, it is easy to create a mini-language modelling a particular business domain. Thanks to these DSLs, developers and subject matter experts will be able to share a common metaphor, to deliver, hand in hand, the final application to the end users.&lt;/p></description><content:encoded>
<![CDATA[<p>I&rsquo;ve just uploaded my <a href="http://javazone.no/incogito/session/Writing+Domain-Specific+Languages+with+Groovy.html">JavaZone</a> slides of the Domain-Specific Language presentation available on Slideshare.</p>
<script async class="speakerdeck-embed" data-id="cde49b5be5f84f5088048076df87c5a8" data-ratio="1.77777777" src="//speakerdeck.com/assets/embed.js"></script>
<blockquote>
<p><strong>Abstract:</strong> Domain-Specific Languages are an &ldquo;architectural hot-spot&rdquo;, as coined recently by InfoQ editors. With dynamic languages such as Groovy, it is easy to create a mini-language modelling a particular business domain. Thanks to these DSLs, developers and subject matter experts will be able to share a common metaphor, to deliver, hand in hand, the final application to the end users.</p></blockquote>
<blockquote>
<p><strong>Outline:</strong> Groovy offers a great deal of flexibility in terms of concise and expressive syntax, and through its dynamic nature. After an introduction to the Groovy dynamic language, and to the concepts of DSLs, their motivation, strenghts and weaknesses, Guillaume Laforge, project lead of Groovy and spec lead of JSR-241 standardizing the Groovy language, will guide you through the advanced Groovy concepts that will help you create your own embedded DSL. He will show you how to integrate them in your JEE application, and also explain how to best design such business languages to ensure their adoption by the end users, their quality, testability, andcapacity to evolve over time to suit the needs of the day. In the IT industry, understanding the domain within which customers evolve is a key factor in the success of a project. From this domain and its wealth of concepts, as software developers and architects, we can derive a design that is aimed at solving problems encountered in the day-to-day business. So far, we mostly solved these brainteasers with computer science paradigms like Object-Oriented Programming, n-tier architectures, or with tools such as rules engines to stay close to the domain at hand. In this session, our goal will be to investigate how the paradigm of Domain-Specific Languages can be leveraged to help our teams build solutions closer to our users&rsquo; needs and requirements, and how it can provide a viable, efficient and expressive alternative problem-solving pattern. The technology of choice for enabling DSL solutions for your projects will be Groovy, a dynamic language for the Java platform.</p></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Griffon shows its claws: Grails-like rich Swing client framework released</title><link>https://glaforge.dev/posts/2008/09/11/griffon-shows-its-claws-grails-like-rich-swing-client-framework-released/</link><pubDate>Thu, 11 Sep 2008 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2008/09/11/griffon-shows-its-claws-grails-like-rich-swing-client-framework-released/</guid><description>&lt;p>The mythical eagle/lion is out the door, with a 0.0 version number! &lt;a href="http://groovy.codehaus.org/Griffon">Griffon&lt;/a> is a Grails-like framework for building rich Swing client applications (applets, webstart, standalone). Andres was &lt;a href="http://www.jroller.com/aalmiray/entry/coming_soon">hinting&lt;/a> at the &lt;a href="http://www.jroller.com/aalmiray/entry/griffon_takes_flight">first release&lt;/a> of this new project, and &lt;a href="http://shemnon.com/speling/2008/09/announcing-griffon-00-1.html">Danno just announced it&lt;/a> officially after having shown some &lt;a href="http://shemnon.com/speling/2008/08/griffon-and-greet-starting-to.html">nice preview&lt;/a> of what it&amp;rsquo;s all about by showing a Twitter client built with Griffon.&lt;/p>
&lt;p>The Groovy swing team&amp;rsquo;s long been hard at work to provide you with powerful declarative Swing UIs with the Groovy Swing builder, and they&amp;rsquo;ve now switched gears to go a big step further by really empowering developers to write, build and distribute clean MVC apps.&lt;/p></description><content:encoded>
<![CDATA[<p>The mythical eagle/lion is out the door, with a 0.0 version number! <a href="http://groovy.codehaus.org/Griffon">Griffon</a> is a Grails-like framework for building rich Swing client applications (applets, webstart, standalone). Andres was <a href="http://www.jroller.com/aalmiray/entry/coming_soon">hinting</a> at the <a href="http://www.jroller.com/aalmiray/entry/griffon_takes_flight">first release</a> of this new project, and <a href="http://shemnon.com/speling/2008/09/announcing-griffon-00-1.html">Danno just announced it</a> officially after having shown some <a href="http://shemnon.com/speling/2008/08/griffon-and-greet-starting-to.html">nice preview</a> of what it&rsquo;s all about by showing a Twitter client built with Griffon.</p>
<p>The Groovy swing team&rsquo;s long been hard at work to provide you with powerful declarative Swing UIs with the Groovy Swing builder, and they&rsquo;ve now switched gears to go a big step further by really empowering developers to write, build and distribute clean MVC apps.</p>
<p>So, now, if you want to learn more about Griffon, the best thing to do is to just follow the white rabbit prey:</p>
<ul>
<li>The <a href="http://groovy.codehaus.org/Griffon">Griffon project page</a></li>
<li><a href="http://groovy.codehaus.org/Installing+Griffon">Downloading and installing Griffon</a></li>
<li>And the, just follow the <a href="http://groovy.codehaus.org/Griffon+Quick+Start">Griffon quick start</a></li>
</ul>
<p>Congratulations to <a href="http://shemnon.com/speling/">Danno</a>, <a href="http://www.jroller.com/aalmiray/">Andres</a> and <a href="http://jameswilliams.be/blog/entry/index">James</a> for their hard work! They&rsquo;ve been <a href="http://twitter.com/aalmiray">tweeting</a> <a href="http://twitter.com/shemnon">like</a> <a href="http://twitter.com/ecspike">mad</a> these past days while getting Griffon ready.</p>
<p><strong>Update:</strong> Geertjan wrote some interesting articles on Griffon:</p>
<ul>
<li><a href="http://groovy.dzone.com/news/hello-griffon">Getting Started with Swing MVC Development on Griffon</a></li>
<li><a href="http://groovy.dzone.com/news/flying-with-griffon">Flying with Griffon</a></li>
<li><a href="http://blogs.sun.com/geertjan/date/20080912">The 5 Best Things About Griffon</a></li>
<li><a href="http://groovy.dzone.com/news/playing-anagrams-griffon">Porting an existing Swing application to Griffon</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy and Grails at the Paris JUG tomorrow night</title><link>https://glaforge.dev/posts/2008/09/08/groovy-and-grails-at-the-paris-jug-tomorrow-night/</link><pubDate>Mon, 08 Sep 2008 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2008/09/08/groovy-and-grails-at-the-paris-jug-tomorrow-night/</guid><description>&lt;p>It&amp;rsquo;s a bit of a late notice, but if you&amp;rsquo;re in Paris these days, you may wish to come to the Paris JUG tomorrow night (Tuesday, September 9th). I&amp;rsquo;ll be presenting an introduction to &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> (the dynamic language for the JVM), as well as a presentation on &lt;a href="http://grails.org/">Grails&lt;/a> (the agile and productive web application framework) with the help of my friend and former colleague &lt;a href="http://blog.octo.com/index.php/2008/09/08/138-paris-jug-groovy-et-grails">Fabrice Robini&lt;/a>.&lt;/p>
&lt;p>You can register &lt;a href="http://jugevents.jugpadova.it/jugevents/event/registration.form?event.id=7939">here&lt;/a> and get more information about the &lt;a href="http://www.parisjug.org/xwiki/bin/view/Meeting/20080909">agenda&lt;/a> of that Paris JUG night.&lt;/p></description><content:encoded>
<![CDATA[<p>It&rsquo;s a bit of a late notice, but if you&rsquo;re in Paris these days, you may wish to come to the Paris JUG tomorrow night (Tuesday, September 9th). I&rsquo;ll be presenting an introduction to <a href="http://groovy.codehaus.org/">Groovy</a> (the dynamic language for the JVM), as well as a presentation on <a href="http://grails.org/">Grails</a> (the agile and productive web application framework) with the help of my friend and former colleague <a href="http://blog.octo.com/index.php/2008/09/08/138-paris-jug-groovy-et-grails">Fabrice Robini</a>.</p>
<p>You can register <a href="http://jugevents.jugpadova.it/jugevents/event/registration.form?event.id=7939">here</a> and get more information about the <a href="http://www.parisjug.org/xwiki/bin/view/Meeting/20080909">agenda</a> of that Paris JUG night.</p>
<p>See you all tomorrow evening!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Knowing which variables are bound or not in a Groovy script</title><link>https://glaforge.dev/posts/2008/08/28/knowing-which-variables-are-bound-or-not-in-a-groovy-script/</link><pubDate>Thu, 28 Aug 2008 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2008/08/28/knowing-which-variables-are-bound-or-not-in-a-groovy-script/</guid><description>&lt;p>A few weeks ago on the Groovy mailing-lists, a user wanted to know a way to find which variables were bound or not in a Groovy script, in the context of some custom rules engine. In a Groovy script, names that are not local variables, method parameters, etc. can come from the &amp;ldquo;binding&amp;rdquo; associated with a script. This is the way we &amp;ldquo;inject&amp;rdquo; variables and values into a script. A usual technique for retrieving variables lazily (for instance when you don&amp;rsquo;t want to put in the binding a variable that is heavy to compute or retrieve) is to create a custom Binding class and override the methods for getting variables from it. But if you really really want to know before executing the scripts (to avoid any side effect upon execution) what variables are bound or not, I&amp;rsquo;ve come up with the following script which lists the bound and unbound variables, without having to execute the script.&lt;/p></description><content:encoded>
<![CDATA[<p>A few weeks ago on the Groovy mailing-lists, a user wanted to know a way to find which variables were bound or not in a Groovy script, in the context of some custom rules engine. In a Groovy script, names that are not local variables, method parameters, etc. can come from the &ldquo;binding&rdquo; associated with a script. This is the way we &ldquo;inject&rdquo; variables and values into a script. A usual technique for retrieving variables lazily (for instance when you don&rsquo;t want to put in the binding a variable that is heavy to compute or retrieve) is to create a custom Binding class and override the methods for getting variables from it. But if you really really want to know before executing the scripts (to avoid any side effect upon execution) what variables are bound or not, I&rsquo;ve come up with the following script which lists the bound and unbound variables, without having to execute the script.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.codehaus.groovy.ast.expr.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.codehaus.groovy.ast.stmt.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.codehaus.groovy.ast.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.codehaus.groovy.control.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.codehaus.groovy.classgen.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">java.security.CodeSource</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// example script to analyze
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> scriptText <span style="color:#666">=</span> <span style="color:#4070a0">&#39;&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">   def foo(args) { args &amp;&amp; !d }
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">   Closure c = { 4 }
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">   int b = 6
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">   try {
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">       println(a + 5 - b + c() / foo(a))
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">   } catch (Throwable t) {
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">       println b
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">   } finally {
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">       throw d
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">   }
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">   assert b
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">&#39;&#39;&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// define a visitor that visits all variable expressions
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">VariableVisitor</span> <span style="color:#007020;font-weight:bold">extends</span> ClassCodeVisitorSupport <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>   <span style="color:#902000">def</span> bound <span style="color:#666">=</span> <span style="color:#666">[]</span> <span style="color:#007020;font-weight:bold">as</span> SortedSet
</span></span><span style="display:flex;"><span>   <span style="color:#902000">def</span> unbound <span style="color:#666">=</span> <span style="color:#666">[]</span> <span style="color:#007020;font-weight:bold">as</span> SortedSet
</span></span><span style="display:flex;"><span>   <span style="color:#902000">void</span> <span style="color:#06287e">visitVariableExpression</span><span style="color:#666">(</span>VariableExpression expression<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>       <span style="color:#60a0b0;font-style:italic">// we&#39;re not interested in some special implicit variables
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>       <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(!(</span>expression<span style="color:#666">.</span><span style="color:#4070a0">variable</span> <span style="color:#007020;font-weight:bold">in</span> <span style="color:#666">[</span><span style="color:#4070a0">&#39;args&#39;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#39;context&#39;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#39;this&#39;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#39;super&#39;</span><span style="color:#666">]))</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>           <span style="color:#60a0b0;font-style:italic">// thanks to this instanceof
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>           <span style="color:#60a0b0;font-style:italic">// we know if the variable is bound or not
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>           <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>expression<span style="color:#666">.</span><span style="color:#4070a0">accessedVariable</span> <span style="color:#007020;font-weight:bold">instanceof</span> DynamicVariable<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>               unbound <span style="color:#666">&lt;&lt;</span> expression<span style="color:#666">.</span><span style="color:#4070a0">variable</span>
</span></span><span style="display:flex;"><span>           <span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">else</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>               bound <span style="color:#666">&lt;&lt;</span> expression<span style="color:#666">.</span><span style="color:#4070a0">variable</span>
</span></span><span style="display:flex;"><span>           <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>       <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>       <span style="color:#007020;font-weight:bold">super</span><span style="color:#666">.</span><span style="color:#4070a0">visitVariableExpression</span><span style="color:#666">(</span>expression<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>   <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   <span style="color:#007020;font-weight:bold">protected</span> SourceUnit <span style="color:#06287e">getSourceUnit</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>       <span style="color:#007020;font-weight:bold">return</span> source<span style="color:#666">;</span>
</span></span><span style="display:flex;"><span>   <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// we define our custom PrimaryClassNodeOperation
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// to be able to hook our code visitor
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">CustomSourceOperation</span> <span style="color:#007020;font-weight:bold">extends</span> CompilationUnit<span style="color:#666">.</span><span style="color:#4070a0">PrimaryClassNodeOperation</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>   CodeVisitorSupport visitor
</span></span><span style="display:flex;"><span>   <span style="color:#902000">void</span> <span style="color:#06287e">call</span><span style="color:#666">(</span>SourceUnit source<span style="color:#666">,</span> GeneratorContext context<span style="color:#666">,</span> ClassNode
</span></span><span style="display:flex;"><span>classNode<span style="color:#666">)</span> <span style="color:#007020;font-weight:bold">throws</span> CompilationFailedException <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>       classNode<span style="color:#666">.</span><span style="color:#4070a0">visitContents</span><span style="color:#666">(</span>visitor<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>   <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// we use our own class loader to add our phase operation
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">MyClassLoader</span> <span style="color:#007020;font-weight:bold">extends</span> GroovyClassLoader <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>   CodeVisitorSupport visitor
</span></span><span style="display:flex;"><span>   <span style="color:#007020;font-weight:bold">protected</span> CompilationUnit
</span></span><span style="display:flex;"><span><span style="color:#06287e">createCompilationUnit</span><span style="color:#666">(</span>CompilerConfiguration config<span style="color:#666">,</span> CodeSource source<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>       CompilationUnit cu <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">super</span><span style="color:#666">.</span><span style="color:#4070a0">createCompilationUnit</span><span style="color:#666">(</span>config<span style="color:#666">,</span> source<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>       cu<span style="color:#666">.</span><span style="color:#4070a0">addPhaseOperation</span><span style="color:#666">(</span><span style="color:#007020;font-weight:bold">new</span> CustomSourceOperation<span style="color:#666">(</span><span style="color:#002070;font-weight:bold">visitor:</span>
</span></span><span style="display:flex;"><span>visitor<span style="color:#666">),</span> Phases<span style="color:#666">.</span><span style="color:#4070a0">CLASS_GENERATION</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>       <span style="color:#007020;font-weight:bold">return</span> cu
</span></span><span style="display:flex;"><span>   <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> visitor <span style="color:#666">=</span>  <span style="color:#007020;font-weight:bold">new</span> VariableVisitor<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> myCL <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> MyClassLoader<span style="color:#666">(</span><span style="color:#002070;font-weight:bold">visitor:</span> visitor<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// simply by parsing the script with our classloader
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// our visitor will be called and will visit all the variables
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> script <span style="color:#666">=</span> myCL<span style="color:#666">.</span><span style="color:#4070a0">parseClass</span><span style="color:#666">(</span>scriptText<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>println <span style="color:#4070a0">&#34;Bound variables:   ${visitor.bound}&#34;</span>
</span></span><span style="display:flex;"><span>println <span style="color:#4070a0">&#34;Unbound variables: ${visitor.unbound}&#34;</span>
</span></span></code></pre></div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Meeting Neil Armstrong and speaking of Groovy and Grails</title><link>https://glaforge.dev/posts/2008/07/15/meeting-neil-armstrong-and-speaking-of-groovy-and-grails/</link><pubDate>Tue, 15 Jul 2008 02:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2008/07/15/meeting-neil-armstrong-and-speaking-of-groovy-and-grails/</guid><description>&lt;p>&lt;figure>
&lt;a href="#img-c633d8f5aee2516deff25fc4abd5fd2a">
&lt;img src="https://glaforge.dev/img/misc/Neil-Armstrong-OCTO-USI.jpg"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-c633d8f5aee2516deff25fc4abd5fd2a">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/Neil-Armstrong-OCTO-USI.jpg"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>I had the pleasure to participate in &lt;a href="http://www.octo.com/">OCTO&lt;/a>&amp;rsquo;s &lt;a href="http://www.universite-du-si.com/">IT University&lt;/a> (Université du SI in French) a couple weeks ago. The conference was really great, and I think France really needed such a high-quality IT event as there was no real good event like that until now. So big kudos to the organizers.&lt;/p>
&lt;p>Apart from great food, excellent speakers covering interesting topics, I had the pleasure of spending time with my friends from the &lt;a href="http://www.ossgtp.org/xwiki/bin/view/Main/WebHome">OSSGTP&lt;/a> community (Vincent, Guillaume, Patrick, Mag, Didier, Fabrice, Erwan), with my former colleagues, and with some great guys like Ross Mason, Erik Meijer (with great tshirts as usual), and more. But perhaps the most impressive, interesting and emotional-heavy moment of the conference was the closing keynote by astronaut &lt;strong>Neil Armstrong&lt;/strong>, as featured on the picture on the side. This man is so humble, interesting, funny, elegant, that everybody really enjoyed listening to him speaking about the space conquest, distilling some nice and funny anecdotes and making parallels with IT, the story of computers, etc. Awesome! It was such a big honnor to have him there!&lt;/p></description><content:encoded>
<![CDATA[<p><figure>
  <a href="#img-c633d8f5aee2516deff25fc4abd5fd2a">
    <img src="/img/misc/Neil-Armstrong-OCTO-USI.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-c633d8f5aee2516deff25fc4abd5fd2a">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/Neil-Armstrong-OCTO-USI.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I had the pleasure to participate in <a href="http://www.octo.com/">OCTO</a>&rsquo;s <a href="http://www.universite-du-si.com/">IT University</a> (Université du SI in French) a couple weeks ago. The conference was really great, and I think France really needed such a high-quality IT event as there was no real good event like that until now. So big kudos to the organizers.</p>
<p>Apart from great food, excellent speakers covering interesting topics, I had the pleasure of spending time with my friends from the <a href="http://www.ossgtp.org/xwiki/bin/view/Main/WebHome">OSSGTP</a> community (Vincent, Guillaume, Patrick, Mag, Didier, Fabrice, Erwan), with my former colleagues, and with some great guys like Ross Mason, Erik Meijer (with great tshirts as usual), and more. But perhaps the most impressive, interesting and emotional-heavy moment of the conference was the closing keynote by astronaut <strong>Neil Armstrong</strong>, as featured on the picture on the side. This man is so humble, interesting, funny, elegant, that everybody really enjoyed listening to him speaking about the space conquest, distilling some nice and funny anecdotes and making parallels with IT, the story of computers, etc. Awesome! It was such a big honnor to have him there!</p>
<p>Back on my presence at the show: I&rsquo;ve given two talks at the conference. The first one on Grails, and how you can <a href="http://www.slideshare.net/glaforge/innover-sans-contrainte-intgrer-sans-rupture">innovate without constraints</a>, while being able to properly integrate your innovations back in your IT infrastructure, using <a href="http://grails.org/">Grails</a>. My friend and former colleague Fabrice Robini was the key driver behind this very well attended and appreciated talk.</p>
<p>And I also spoke about <a href="http://www.slideshare.net/glaforge/domainspecific-languages-avec-groovy">Domain-Specific Languages</a> and how Groovy can help you create your own.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy and Grails trainings by G2One in North American</title><link>https://glaforge.dev/posts/2008/07/15/groovy-and-grails-trainings-by-g2one-in-north-american/</link><pubDate>Tue, 15 Jul 2008 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2008/07/15/groovy-and-grails-trainings-by-g2one-in-north-american/</guid><description>&lt;p>If you&amp;rsquo;re in the &lt;strong>North America&lt;/strong> and you want to get up to speed with Groovy and Grails, &lt;a href="http://www.g2one.com/">G2One&lt;/a>, the Groovy/Grails company, has just announced its &lt;a href="http://www.g2one.com/training/">updated training schedule&lt;/a> for this year.&lt;/p>
&lt;p>From August to November, we&amp;rsquo;ll be in:&lt;/p>
&lt;ul>
&lt;li>San Jose (CA)&lt;/li>
&lt;li>Chicago (IL)&lt;/li>
&lt;li>Orange County (CA)&lt;/li>
&lt;li>Austin (TX)&lt;/li>
&lt;li>Calgary (Canada)&lt;/li>
&lt;li>and Atlanta (GA)&lt;/li>
&lt;/ul>
&lt;p>You can find the &lt;a href="http://www.g2one.com/training/">exact dates online&lt;/a>. And you can already register for the &lt;a href="http://www.acteva.com/booking.cfm?bevaid=163134">San Jose&lt;/a> and &lt;a href="http://www.acteva.com/booking.cfm?bevaid=163135">Chicago&lt;/a> dates in August.&lt;/p></description><content:encoded>
<![CDATA[<p>If you&rsquo;re in the <strong>North America</strong> and you want to get up to speed with Groovy and Grails, <a href="http://www.g2one.com/">G2One</a>, the Groovy/Grails company, has just announced its <a href="http://www.g2one.com/training/">updated training schedule</a> for this year.</p>
<p>From August to November, we&rsquo;ll be in:</p>
<ul>
<li>San Jose (CA)</li>
<li>Chicago (IL)</li>
<li>Orange County (CA)</li>
<li>Austin (TX)</li>
<li>Calgary (Canada)</li>
<li>and Atlanta (GA)</li>
</ul>
<p>You can find the <a href="http://www.g2one.com/training/">exact dates online</a>. And you can already register for the <a href="http://www.acteva.com/booking.cfm?bevaid=163134">San Jose</a> and <a href="http://www.acteva.com/booking.cfm?bevaid=163135">Chicago</a> dates in August.</p>
<p>These public trainings are the ideal opportunity to learn those technologies by listening to the guys who actually provide them: you learn Groovy and Grails right from the horse mouth! No other horses like us understand Groovy and Grails better! Also, with a nice mix of lecture and hands-on labs, there&rsquo;s no doubt you&rsquo;ll become an expert soon.</p>
<p>For further information, beyond the <a href="http://www.g2one.com/training/">G2One site&rsquo;s training section</a>, you can have a look at the <a href="http://www.g2one.com/training/GroovyGrails_3_Day_Workshop_Overview.pdf">overal course description</a> to know what&rsquo;s inside the tin.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>The IT conference you can't miss if you're in Paris!</title><link>https://glaforge.dev/posts/2008/06/24/the-it-conference-you-can-t-miss-if-you-re-in-paris/</link><pubDate>Tue, 24 Jun 2008 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2008/06/24/the-it-conference-you-can-t-miss-if-you-re-in-paris/</guid><description>&lt;p>&lt;a href="http://www.universite-du-si.com/">&lt;/a>&lt;/p>
&lt;p>If you&amp;rsquo;re in Paris, next week, make sure you don&amp;rsquo;t miss the &amp;ldquo;&lt;a href="http://www.universite-du-si.com/">Université du SI&lt;/a>&amp;rdquo; (IT University) conference, organized by my former colleagues from &lt;a href="http://www.octo.com/">OCTO Technology&lt;/a>. This conference is going to rock! Quite frankly, I think France was seriously lacking a good IT conference, and I think OCTO is going to deliver on that promise. I&amp;rsquo;ve been told they are pretty soon running out of entry passes as we&amp;rsquo;re close to the deadline, so it&amp;rsquo;s your &lt;a href="http://www.universite-du-si.com/Inscription.aspx">last chance to register&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://www.universite-du-si.com/"></a></p>
<p>If you&rsquo;re in Paris, next week, make sure you don&rsquo;t miss the &ldquo;<a href="http://www.universite-du-si.com/">Université du SI</a>&rdquo; (IT University) conference, organized by my former colleagues from <a href="http://www.octo.com/">OCTO Technology</a>. This conference is going to rock! Quite frankly, I think France was seriously lacking a good IT conference, and I think OCTO is going to deliver on that promise. I&rsquo;ve been told they are pretty soon running out of entry passes as we&rsquo;re close to the deadline, so it&rsquo;s your <a href="http://www.universite-du-si.com/Inscription.aspx">last chance to register</a>.</p>
<p>If you want to hear <a href="http://www.universite-du-si.com/Speakers.aspx#Neil_Armstrong">Neil Armstrong</a> speak about how he went to the moon, listen to a famous <a href="http://www.universite-du-si.com/Speakers.aspx#Michel_Serres">French philosopher</a>, hear what <a href="http://www.universite-du-si.com/Speakers.aspx#Bjarne_Stroustrup">C++ inventor Bjarne Stroustrup</a>has to say, you&rsquo;ve knocked at the right door. The <a href="http://www.universite-du-si.com/Speakers.aspx">line-up of speakers</a> is pretty impressive. The <a href="http://www.universite-du-si.com/ParcoursLibre.aspx">various presentations</a> cover a wide range of very interesting topics, under 4 main categories:</p>
<ul>
<li>Architecture,</li>
<li>IT Governance,</li>
<li>Methodologies, and</li>
<li>Technologies.</li>
</ul>
<p>I&rsquo;ll personally have the pleasure and honor to also speak there, and will cover my two pet topics: <a href="http://groovy.codehaus.org/">Groovy</a> and its ability to let you write Domain-Specific Languages, and <a href="http://grails.org/">Grails</a> for helping people be more productive when developing business apps.</p>
<p>Here are the abstracts of my two talks (in French, I&rsquo;m sorry!):</p>
<ul>
<li>
<p><a href="http://www.universite-du-si.com/ParcoursLibre.aspx#domain-specific-languages">Les Domain-Specific Languages avec Groovy</a><br />
Les Domain-Specific Languages sont des artifacts architecturaux à la mode, comme l&rsquo;indiquait récemment un rédacteur sur InfoQ. Avec les langages dynamique tels que Groovy, il est aisé de créer un mini-langage modélisant un domaine d&rsquo;expertise particulier. Grâce à ces DSLs, développeurs et experts métier seront capable de partager une métaphore commune, de délivrer, main dans la main, l&rsquo;application aux utilisateurs finaux. Groovy offre un niveau élevé de flexibilité en termes d&rsquo;expressivité et de concision de sa syntaxe et au travers de sa nature de langage dynamique. Après une introduction aux concepts de DSLs et leur motivation, Guillaume Laforge, chef de projet de Groovy et spec lead du JSR-251 standardisant le langage Groovy, vous guidera au travers des concepts avancés de Groovy vous permettant de créer votre propre mini-langage métier. Il vous montrera comment intégrer ces langages dans vos applications Java EE, et expliquera également comment au mieux définir ces langages métier, pour s&rsquo;assurer leur adoption par les utilisateurs finaux, leur qualité, leur testabilité, et leur capacité à évoluer au cours du temps pour répondre au mieux aux besoins du jour, ` toujours en perpétuelle évolution.</p>
</li>
<li>
<p><a href="http://www.universite-du-si.com/ParcoursLibre.aspx#technologies-non-mainstream-standards-dsi">T18 - Innover sans contraintes, intégrer sans ruptures</a><br />
Le business case est innovant et les contraintes métier imposent des délais serrés&hellip; Pour répondre à ce besoin, il existe des solutions open-source &ldquo;intégrées&rdquo; permettant d&rsquo;être très rapidement opérationnel et d&rsquo;accélérer les développements. Comment favoriser l&rsquo;innovation sans contraintes et garantir une intégration sans rupture dans le S.I dans le respect de ses normes et standards !? Retour d&rsquo;expérience sur <a href="http://grails.org/">Grails</a>, framework de développement d’applications Web sur la JVM.</p>
</li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 1.6-beta-1 release with great performance improvements</title><link>https://glaforge.dev/posts/2008/05/03/groovy-1-6-beta-1-release-with-great-performance-improvements/</link><pubDate>Sat, 03 May 2008 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2008/05/03/groovy-1-6-beta-1-release-with-great-performance-improvements/</guid><description>&lt;p>This is with great pleasure that &lt;a href="http://www.g2one.com/">G2One&lt;/a> and the Groovy development team announce the &lt;strong>first beta of Groovy 1.6&lt;/strong>.&lt;/p>
&lt;p>Beyond the &lt;strong>73 bug fixes&lt;/strong> and &lt;strong>24 improvements&lt;/strong> listed in the release notes above, the main focus was on performance improvements.&lt;/p>
&lt;h2 id="compilation-and-runtime-performance-improvements">Compilation and runtime performance improvements&lt;/h2>
&lt;p>As you may have already noticed with Groovy 1.5.5, &lt;strong>the compiler is 3 to 5 times faster&lt;/strong>than in previous releases. This improvement is available both in this development version and in the 1.5.x stable branch. Thanks to class lookup caches, the bigger the project, the faster the compilation will be.&lt;/p></description><content:encoded>
<![CDATA[<p>This is with great pleasure that <a href="http://www.g2one.com/">G2One</a> and the Groovy development team announce the <strong>first beta of Groovy 1.6</strong>.</p>
<p>Beyond the <strong>73 bug fixes</strong> and <strong>24 improvements</strong> listed in the release notes above, the main focus was on performance improvements.</p>
<h2 id="compilation-and-runtime-performance-improvements">Compilation and runtime performance improvements</h2>
<p>As you may have already noticed with Groovy 1.5.5, <strong>the compiler is 3 to 5 times faster</strong>than in previous releases. This improvement is available both in this development version and in the 1.5.x stable branch. Thanks to class lookup caches, the bigger the project, the faster the compilation will be.</p>
<p>However, the most noticeable changes will be in the general runtime performance improvements of Groovy. We used several benchmarks from the Great Language Shootout to measure our progress. On those we selected, compared to the current Groovy 1.5.6 stable release, the <strong>performance improvements range from 150% to 460%</strong>. Micro-benchmarks obviously don&rsquo;t reflect the kind of code you have in your own projects, but the overal performance of your projects should improve significantly.</p>
<p>Beyond delivering stable and quality releases, <strong>our main focus over the past 10 months has clearly been on performance</strong>.<br />
Between Groovy 1.0 and 1.5.1, on these same tests, we had already gained up to 80% speed improvements, and even between &ldquo;dot releases&rdquo; (1.5.1 and 1.5.6) we gained again up to 40% more. However, it&rsquo;s really in the development branch that we&rsquo;ve integrated advanced call site caching techniques and bytecode diets in the runtime to get the 150-460% speed improvements mentioned above.</p>
<h2 id="important-new-features">Important new features</h2>
<p>Apart from performance related work, bug fixing and minor improvements, let me highlight two new key features which are under development.</p>
<h2 id="multiple-assignments">Multiple assignments</h2>
<p>First of all, I&rsquo;ll mention <strong>multiple assignments</strong>.</p>
<p>A code sample is always worth a thousand words:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> <span style="color:#06287e">listOfN</span><span style="color:#666">(</span>numOfElem<span style="color:#666">)</span> <span style="color:#666">{</span> <span style="color:#40a070">1</span><span style="color:#666">..</span><span style="color:#4070a0">numOfElem</span> <span style="color:#666">}</span> 
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> a<span style="color:#666">,</span> b 
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// variables will contain each successive element of the list 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// and variables beyond the count of elements will be null 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#666">[</span>a<span style="color:#666">,</span> b<span style="color:#666">]</span> <span style="color:#666">=</span> listOfN<span style="color:#666">(</span><span style="color:#40a070">1</span><span style="color:#666">)</span> 
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// a list of one element 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">assert</span> a <span style="color:#666">==</span> <span style="color:#40a070">1</span> <span style="color:#007020;font-weight:bold">assert</span> b <span style="color:#666">==</span> <span style="color:#007020;font-weight:bold">null</span> 
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// if there are more elements, they won&#39;t be assigned to any variable 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#666">[</span>a<span style="color:#666">,</span> b<span style="color:#666">]</span> <span style="color:#666">=</span> listOfN<span style="color:#666">(</span><span style="color:#40a070">10</span><span style="color:#666">)</span> 
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// a list of ten elements 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">assert</span> a <span style="color:#666">==</span> <span style="color:#40a070">1</span> 
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> b <span style="color:#666">==</span> <span style="color:#40a070">2</span> 
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// and you can swap variables with this notation 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#666">[</span>a<span style="color:#666">,</span> b<span style="color:#666">]</span> <span style="color:#666">=</span> <span style="color:#666">[</span>b<span style="color:#666">,</span> a<span style="color:#666">]</span> 
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> a <span style="color:#666">==</span> <span style="color:#40a070">2</span> 
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> b <span style="color:#666">==</span> <span style="color:#40a070">1</span>
</span></span></code></pre></div><h2 id="ast-transformations">AST Transformations</h2>
<p>The other key feature are the <strong>AST Transformations</strong>. It is more of an advanced feature that is useful for people knowing the internals of Groovy. But fortunately, some practical transformations can be of great interest for Swing developers, for instance.</p>
<p>When the Groovy compiler compiles Groovy scripts and classes, at some point in the process, the source code will end up being represented in memory in the form of a Concrete Syntax Tree, then transformed into an Abstract Syntax Tree. The purpose of AST Transformations is to let developers hook into the compilation process to be able to modify the AST before it is turned into bytecode that will be run by the JVM. Using annotations to decorate certain classes, fields or methods, a transformation can be applied to the AST of these elements.</p>
<p>A concrete example of the kind of transformations we can achieve with AST Transformations is the new @Bindable annotation. This is particularly useful if you are a Swing developer. See this example:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">MyBean</span> <span style="color:#666">{</span> <span style="color:#555;font-weight:bold">@Bindable</span> String pro <span style="color:#666">}</span>
</span></span></code></pre></div><p>When you decorate a field with @Bindable, property change listener methods will be transparently added to the bytecode of the class, so that you are able to monitor changes to the value of this field without having to manually write those methods yourself. No need to create an addPropertyListener() and removePropertyListener() method anymore, to fire property change events manually in your property setters, etc.</p>
<p>Danno Ferrin gives us an <a href="http://shemnon.com/speling/2008/04/bindable-observable-properties.html">interesting overview of this annotation</a> on his blog.</p>
<p>Other useful and interesting transformations and annotations will be coming in the future.</p>
<h2 id="annotation-definition">Annotation definition</h2>
<p>Last but not least, as we mentioned annotations above, Groovy 1.5 was still lacking the ability to let us create annotations in Groovy &ndash; they still had to be written in Java. Now it&rsquo;s possible to <strong>define annotations in Groovy</strong> itself with the usual Java syntax.</p>
<h2 id="noteworthy-remarks">Noteworthy remarks</h2>
<p>Groovy 1.6-beta-1 is built against JDK 5, but we will provide JDK 1.4 &ldquo;retro-translated&rdquo; versions of Groovy.<br />
Note however that you may not necessarily experience the same performance improvements when running on JDK 1.4, as certain enhancements take advantage of JDK 5 (util.concurrent VM optimizations, for instance).<br />
The bytecode generated by the Groovy compiler, as before, still targets the 1.4 JVM bytecode, except for annotations, enums and generics.</p>
<h2 id="useful-links">Useful links</h2>
<p>You can download this new beta here:<br />
<a href="http://groovy.codehaus.org/Download">http://groovy.codehaus.org/Download</a></p>
<p>And read the release notes from JIRA here:<br />
<a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;styleName=Html&amp;version=14008">http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;styleName=Html&amp;version=14008</a></p>
<h2 id="conclusion">Conclusion</h2>
<p>Thanks a lot to all those who have helped us making this release: users, contributors, committers, G2One employees.<br />
This performant and innovative new version wouldn&rsquo;t be the same without all your hard work.<br />
Special thanks to Paul King, Danno Ferrin, Alex Tkachman and Jochen &ldquo;blackdrag&rdquo; Theodorou for their commitment and quality work.</p>
<p>We&rsquo;re <strong>very interested in hearing about your feedback</strong> on this release.<br />
Even if it&rsquo;s not the final 1.6 stable release and that you stick with 1.5.x in production, we would like to know how this beta performs in your respective projects, whether you notice any problem or regression. Please report anything you may find.</p>
<p>Enjoy!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy / Grails meetup, next Monday evening, in San Francisco</title><link>https://glaforge.dev/posts/2008/05/01/groovy-grails-meetup-next-monday-evening-in-san-francisco/</link><pubDate>Thu, 01 May 2008 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2008/05/01/groovy-grails-meetup-next-monday-evening-in-san-francisco/</guid><description>&lt;p>If you&amp;rsquo;re going to JavaOne this year, and if you&amp;rsquo;re interested in Groovy and Grails, you can&amp;rsquo;t miss &lt;a href="http://www.g2one.com/">G2One&lt;/a>&amp;rsquo;s free &lt;a href="http://www.g2one.com/meetup/">Groovy / Grails meetup&lt;/a>, Monday evening, 7pm, at the W Hotel, across the street from Moscone.&lt;/p>
&lt;p>It&amp;rsquo;s going to be a great opportunity to meet the guys behind both &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> and &lt;a href="http://grails.org/">Grails&lt;/a>, learn about the latest news about these projects, see some cool demos, and hear about customers who&amp;rsquo;ve decided to use Grails in their projects. You&amp;rsquo;ll be able to also get great Groovy books signed by authors &lt;a href="http://www.pragprog.com/titles/vslg/programming-groovy">Venkat Subramaniam&lt;/a> and &lt;a href="http://www.pragprog.com/titles/sdgrvr/groovy-recipes">Scott Davis&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>If you&rsquo;re going to JavaOne this year, and if you&rsquo;re interested in Groovy and Grails, you can&rsquo;t miss <a href="http://www.g2one.com/">G2One</a>&rsquo;s free <a href="http://www.g2one.com/meetup/">Groovy / Grails meetup</a>, Monday evening, 7pm, at the W Hotel, across the street from Moscone.</p>
<p>It&rsquo;s going to be a great opportunity to meet the guys behind both <a href="http://groovy.codehaus.org/">Groovy</a> and <a href="http://grails.org/">Grails</a>, learn about the latest news about these projects, see some cool demos, and hear about customers who&rsquo;ve decided to use Grails in their projects. You&rsquo;ll be able to also get great Groovy books signed by authors <a href="http://www.pragprog.com/titles/vslg/programming-groovy">Venkat Subramaniam</a> and <a href="http://www.pragprog.com/titles/sdgrvr/groovy-recipes">Scott Davis</a>.</p>
<p>After an hour and half of keynote, testimonials and cool demos, drinks and apetizers will be provided, and after the break will take place the <a href="http://www.nofluffjuststuff.com/nfjsmeetupJ1.jsp">NFJS meeting</a> with an interesting panel discussion.</p>
<p>So, to recap, we&rsquo;re pleased to invite you to the free <a href="http://www.g2one.com/meetup/">Groovy/Grails meetup</a>, on Monday 5th, at 7pm, at the W Hotel, across the street from Moscone.</p>
<p>Please make sure to <a href="http://www.g2one.com/meetup/registration/register">register</a> to confirm your presence.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Grails, nominated for JAX innovation award</title><link>https://glaforge.dev/posts/2008/04/21/grails-nominated-for-jax-innovation-award/</link><pubDate>Mon, 21 Apr 2008 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2008/04/21/grails-nominated-for-jax-innovation-award/</guid><description>&lt;p>This week takes place the 2008 &lt;a href="http://it-republik.de/jaxenter/jax/">JAX conference&lt;/a>, in Germany. Last year, &lt;a href="http://docs.codehaus.org/display/GROOVY/2007/04/26/Groovy+wins+first+prize+at+JAX+2007+innovation+award">Groovy won the first prize&lt;/a> of the JAX innovation award, and this year, Grails was submitted and has just been &lt;a href="http://www.jaxmag.com/itr/news/psecom,id,41565,nodeid,146.html">nominated by the jury&lt;/a>.&lt;a href="http://www.g2one.com/company.html#guillaume">Guillaume Laforge&lt;/a>, Groovy project lead and initiator of the Grails project, will be at JAX this week and will represent the Grails team and project.&lt;/p>
&lt;p>From the nomination announcement:&lt;/p>
&lt;blockquote>
&lt;p>Now it is official: The ten nominees for the JAX Innovation Award. The winners of the award are kept top secret and will be made public next week at the JAX. The award, which is endowed with price money of 20,000 Euro altogether, originated from an initiative of the Software &amp;amp; Support publishing company.&lt;/p></description><content:encoded>
<![CDATA[<p>This week takes place the 2008 <a href="http://it-republik.de/jaxenter/jax/">JAX conference</a>, in Germany. Last year, <a href="http://docs.codehaus.org/display/GROOVY/2007/04/26/Groovy+wins+first+prize+at+JAX+2007+innovation+award">Groovy won the first prize</a> of the JAX innovation award, and this year, Grails was submitted and has just been <a href="http://www.jaxmag.com/itr/news/psecom,id,41565,nodeid,146.html">nominated by the jury</a>.<a href="http://www.g2one.com/company.html#guillaume">Guillaume Laforge</a>, Groovy project lead and initiator of the Grails project, will be at JAX this week and will represent the Grails team and project.</p>
<p>From the nomination announcement:</p>
<blockquote>
<p>Now it is official: The ten nominees for the JAX Innovation Award. The winners of the award are kept top secret and will be made public next week at the JAX. The award, which is endowed with price money of 20,000 Euro altogether, originated from an initiative of the Software &amp; Support publishing company.</p>
<p>Among many submissions from all over Europe our jury has selected these ten contributions. The contribution&rsquo;s innovative value, creativity, elegance and relevancy for practice have been the assessment criteria of the jury.</p>
<ul>
<li>Apache Sling</li>
<li>Biometric Authentification</li>
<li>GIANTsuite AdCreator</li>
<li>Grails</li>
<li>GreenFire</li>
<li>Intrexx Xtreme 4</li>
<li>Java business transaction performance management across the entire application lifecycle</li>
<li>jBPM</li>
<li>PETA</li>
<li>Visual Rules Business Rules Management System</li>
</ul>
<p>It is the intention of the JAX Innovation Award to place inspiring innovations from Europe in the limelight and thus help them to get their chance of success. All interested people can enter this contest. Contributions could be submitted by companies but also by individual people, from research and education or from other organizations that contribute to Java, Eclipse or SOA with their project.</p></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 1.5.5 released: compiler 3-5x faster</title><link>https://glaforge.dev/posts/2008/04/14/groovy-1-5-5-released-compiler-3-5x-faster/</link><pubDate>Mon, 14 Apr 2008 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2008/04/14/groovy-1-5-5-released-compiler-3-5x-faster/</guid><description>&lt;p>&lt;a href="http://www.g2one.com/">G2One, Inc.&lt;/a> and the Groovy development team are pleased to announce the release of &lt;strong>Groovy 1.5.5&lt;/strong>, a bug fix release of the 1.5.x stable branch.&lt;/p>
&lt;p>Beyond all the &lt;a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;amp;styleName=Html&amp;amp;version=14028">bug fixes and consistency improvements&lt;/a>, the major aspect of this release is certainly the improvements in compilation speed. As part of our &lt;strong>ongoing efforts to improve the performance of Groovy&lt;/strong>, we have worked hard on compilation speed, and we backported those improvements from the upcoming Groovy 1.6, to ensure that all users using stable versions of Groovy can benefit from these performance increases. The &lt;strong>Groovyc compiler should now be from 3 to 5 times faster&lt;/strong>, which will make big Groovy / Java and Grails projects much snappier to compile.&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://www.g2one.com/">G2One, Inc.</a> and the Groovy development team are pleased to announce the release of <strong>Groovy 1.5.5</strong>, a bug fix release of the 1.5.x stable branch.</p>
<p>Beyond all the <a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;styleName=Html&amp;version=14028">bug fixes and consistency improvements</a>, the major aspect of this release is certainly the improvements in compilation speed. As part of our <strong>ongoing efforts to improve the performance of Groovy</strong>, we have worked hard on compilation speed, and we backported those improvements from the upcoming Groovy 1.6, to ensure that all users using stable versions of Groovy can benefit from these performance increases. The <strong>Groovyc compiler should now be from 3 to 5 times faster</strong>, which will make big Groovy / Java and Grails projects much snappier to compile.</p>
<p>A first beta of the upcoming Groovy 1.6 will follow in the coming weeks, and the team focus is still on performance improvements. Groovy 1.6 contains this enhanced compiler, but also brings runtime improvements in many areas, including on number arithmetics where Groovy was known to be slow.</p>
<p>Go <a href="http://groovy.codehaus.org/Download">download Groovy 1.5.5</a> and read the <a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;styleName=Html&amp;version=14028">changelog</a> for more details on the release.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy / Grails support in NetBeans and GlassFish</title><link>https://glaforge.dev/posts/2008/03/23/groovy-grails-support-in-netbeans-and-glassfish/</link><pubDate>Sun, 23 Mar 2008 02:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2008/03/23/groovy-grails-support-in-netbeans-and-glassfish/</guid><description>&lt;p>Sun engineer Matthias Schmidt has just published an article on the progress of the &lt;a href="http://blogs.sun.com/schmidtm/entry/news_from_grailsland">Groovy and Grails support in NetBeans&lt;/a>. The &lt;a href="http://blogs.sun.com/theaquarium/">Aquarium&lt;/a> also features the ongoing work on support of &lt;a href="http://blogs.sun.com/theaquarium/entry/netbeans_support_for_groovy">Grails in Glassfish&lt;/a>.&lt;/p>
&lt;p>On NetBeans front, Matthias Schmidt and Martin Adamek started working on a plugin back in November. You&amp;rsquo;ll need to use a &lt;a href="http://deadlock.netbeans.org/hudson/job/trunk/lastSuccessfulBuild/artifact/nbbuild/dist/zip/">NetBeans nightly build&lt;/a>, and download the Groovy/Grails plugin from the updace center. The plugin already provides:&lt;/p>
&lt;ul>
&lt;li>Method-completion including JavaDoc display for Groovy and Java&lt;/li>
&lt;li>Code Folding of Groovy source files&lt;/li>
&lt;li>Starting, stopping of the Grails server&lt;/li>
&lt;li>Importing existing Grails projects with a week arranged display of project structure&lt;/li>
&lt;li>Groovy/Grails module settings integrated into NetBeans options dialog&lt;/li>
&lt;li>Marking of source code errors&lt;/li>
&lt;li>Easy navigation of Groovy source code by using a navigator view&lt;/li>
&lt;li>Customizing of Grails environment and server port&lt;/li>
&lt;li>Auto-deploy to the Glassfish application server&lt;/li>
&lt;li>Starting common Grails tasks from context menu&lt;/li>
&lt;li>Status of running Grails server displayed in status-line&lt;/li>
&lt;li>Syntax highlighting&lt;/li>
&lt;/ul>
&lt;p>This is a promising beginning, but there&amp;rsquo;s definitely more to come:&lt;/p></description><content:encoded>
<![CDATA[<p>Sun engineer Matthias Schmidt has just published an article on the progress of the <a href="http://blogs.sun.com/schmidtm/entry/news_from_grailsland">Groovy and Grails support in NetBeans</a>. The <a href="http://blogs.sun.com/theaquarium/">Aquarium</a> also features the ongoing work on support of <a href="http://blogs.sun.com/theaquarium/entry/netbeans_support_for_groovy">Grails in Glassfish</a>.</p>
<p>On NetBeans front, Matthias Schmidt and Martin Adamek started working on a plugin back in November. You&rsquo;ll need to use a <a href="http://deadlock.netbeans.org/hudson/job/trunk/lastSuccessfulBuild/artifact/nbbuild/dist/zip/">NetBeans nightly build</a>, and download the Groovy/Grails plugin from the updace center. The plugin already provides:</p>
<ul>
<li>Method-completion including JavaDoc display for Groovy and Java</li>
<li>Code Folding of Groovy source files</li>
<li>Starting, stopping of the Grails server</li>
<li>Importing existing Grails projects with a week arranged display of project structure</li>
<li>Groovy/Grails module settings integrated into NetBeans options dialog</li>
<li>Marking of source code errors</li>
<li>Easy navigation of Groovy source code by using a navigator view</li>
<li>Customizing of Grails environment and server port</li>
<li>Auto-deploy to the Glassfish application server</li>
<li>Starting common Grails tasks from context menu</li>
<li>Status of running Grails server displayed in status-line</li>
<li>Syntax highlighting</li>
</ul>
<p>This is a promising beginning, but there&rsquo;s definitely more to come:</p>
<ul>
<li>Debugging support</li>
<li>MultiView for easy navigation between corresponding Model-View-Controller files</li>
<li>Refactoring support</li>
</ul>
<p>On GlassFish&rsquo;s side, Eduardo Pelegri <a href="http://blogs.sun.com/theaquarium/entry/netbeans_support_for_groovy">reports</a> improvements and bug fixes for <a href="http://blogs.sun.com/theaquarium/entry/kicking_the_tires_of_grails">running Grails applications in GlassFish</a> and shares a link to the <a href="http://wiki.glassfish.java.net/Wiki.jsp?page=GroovyGrailsPlanning">roadmap of the Groovy/Grails support in GlassFish</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>JSON.Net, the Groovy way</title><link>https://glaforge.dev/posts/2008/03/23/json-net-the-groovy-way/</link><pubDate>Sun, 23 Mar 2008 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2008/03/23/json-net-the-groovy-way/</guid><description>&lt;p>On &lt;a href="http://ajaxian.com/archives/jsonnet-20">Ajaxian&lt;/a>, the other day, I spotted an article about &lt;a href="http://www.codeplex.com/Json">JSON.Net&lt;/a>, a project aiming at simplifying the production and consumption of JSON data for the .Net world, and I wanted to contrast what I&amp;rsquo;ve read with what we are doing with &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> and &lt;a href="http://grails.org/">Grails&lt;/a>. I rarely speak about the Microsoft world, but the latest features of C# 3 are very interesting and powerful, particularly the anonymous types, their closures (whatever they are called), and LINQ for querying relational or tree structured data.&lt;/p></description><content:encoded>
<![CDATA[<p>On <a href="http://ajaxian.com/archives/jsonnet-20">Ajaxian</a>, the other day, I spotted an article about <a href="http://www.codeplex.com/Json">JSON.Net</a>, a project aiming at simplifying the production and consumption of JSON data for the .Net world, and I wanted to contrast what I&rsquo;ve read with what we are doing with <a href="http://groovy.codehaus.org/">Groovy</a> and <a href="http://grails.org/">Grails</a>. I rarely speak about the Microsoft world, but the latest features of C# 3 are very interesting and powerful, particularly the anonymous types, their closures (whatever they are called), and LINQ for querying relational or tree structured data.</p>
<p>For instance, here&rsquo;s how JSON.Net produces JSON content:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-csharp" data-lang="csharp"><span style="display:flex;"><span>JObject o = JObject.FromObject(<span style="color:#007020;font-weight:bold">new</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    channel = <span style="color:#007020;font-weight:bold">new</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        title = <span style="color:#4070a0">&#34;James Newton-King&#34;</span>,
</span></span><span style="display:flex;"><span>        link = <span style="color:#4070a0">&#34;http://james.newtonking.com&#34;</span>,
</span></span><span style="display:flex;"><span>        description = <span style="color:#4070a0">&#34;James Newton-King&#39;s blog.&#34;</span>,
</span></span><span style="display:flex;"><span>        item =
</span></span><span style="display:flex;"><span>            <span style="color:#007020;font-weight:bold">from</span> p <span style="color:#007020;font-weight:bold">in</span> posts
</span></span><span style="display:flex;"><span>            <span style="color:#007020;font-weight:bold">orderby</span> p.Title
</span></span><span style="display:flex;"><span>            <span style="color:#007020;font-weight:bold">select</span> <span style="color:#007020;font-weight:bold">new</span>
</span></span><span style="display:flex;"><span>            {
</span></span><span style="display:flex;"><span>                title = p.Title,
</span></span><span style="display:flex;"><span>                description = p.Description,
</span></span><span style="display:flex;"><span>                link = p.Link,
</span></span><span style="display:flex;"><span>                category = p.Categories
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    });
</span></span></code></pre></div><p>Here, we can see the new anonymous type feature, with the new {} construct to easily create new data structure, without requiring the creation of classes or interfaces. And in the item element, we notice LINQ at work providing an SQL like notation to select the posts ordered by title.</p>
<p>In Groovy and Grails land, we are reusing the map notation to <a href="http://grails.org/doc/1.0.x/guide/6.%20The%20Web%20Layer.html#6.1.7%20XML%20and%20JSON%20Responses">create JSON content</a>, that we then coerce to JSON using the as operator:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">grails.converters.JSON</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> data <span style="color:#666">=</span> <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>    <span style="color:#002070;font-weight:bold">channel:</span> <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>        <span style="color:#002070;font-weight:bold">title:</span> <span style="color:#4070a0">&#34;James Newton-King&#34;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>        <span style="color:#002070;font-weight:bold">link:</span> <span style="color:#4070a0">&#34;http://james.newtonking.com&#34;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>        <span style="color:#002070;font-weight:bold">description:</span> <span style="color:#4070a0">&#34;James Newton-King&#39;s blog.&#34;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>        <span style="color:#002070;font-weight:bold">item:</span> 
</span></span><span style="display:flex;"><span>            posts<span style="color:#666">.</span><span style="color:#4070a0">sort</span> <span style="color:#666">{</span> it<span style="color:#666">.</span><span style="color:#4070a0">title</span> <span style="color:#666">}.</span><span style="color:#4070a0">collect</span> <span style="color:#666">{</span> 
</span></span><span style="display:flex;"><span>            <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>                <span style="color:#002070;font-weight:bold">title:</span> it<span style="color:#666">.</span><span style="color:#4070a0">title</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>                <span style="color:#002070;font-weight:bold">description:</span> it<span style="color:#666">.</span><span style="color:#4070a0">description</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>                <span style="color:#002070;font-weight:bold">link:</span> it<span style="color:#666">.</span><span style="color:#4070a0">link</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>                <span style="color:#002070;font-weight:bold">category:</span> it<span style="color:#666">.</span><span style="color:#4070a0">categories</span>
</span></span><span style="display:flex;"><span>            <span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">]</span>
</span></span><span style="display:flex;"><span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// then, if you want to render this structure as JSON inside a Grails controller:
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>render data <span style="color:#007020;font-weight:bold">as</span> JSON
</span></span></code></pre></div><p>Unlike LINQ with its SQL-like notation, Groovy favors a more functional approach, using the sort() and collect() taking closures to do the filtering and aggregation. These methods are added by the Groovy Development Kit to the usual Java collection classes.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A Domain-Specific Language for unit manipulations</title><link>https://glaforge.dev/posts/2008/03/01/a-domain-specific-language-for-unit-manipulations/</link><pubDate>Sat, 01 Mar 2008 00:20:00 +0100</pubDate><guid>https://glaforge.dev/posts/2008/03/01/a-domain-specific-language-for-unit-manipulations/</guid><description>&lt;p>Domain-Specific Languages are a hot topic, and have been popularized by languages like &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> and Ruby thanks to their malleable syntax which make them a great fit for this purpose. In particular, &lt;strong>Groovy allows you to create internal DSLs&lt;/strong>: business languages hosted by Groovy. In a recent research work, Tiago Antão has &lt;a href="http://tiago.org/ps/2008/02/24/groovyscalarubypython-on-jvm/">decided to use Groovy&lt;/a> to &lt;strong>model the resistance to drugs against the Malaria disease&lt;/strong>. In two blog posts, Tiago explains some of the &lt;a href="http://tiago.org/ps/2008/02/25/dsl-tactics-in-groovy-1many/">tactics he used&lt;/a>, and how to &lt;a href="http://tiago.org/ps/2008/02/27/chloroquine-malaria-treatment-and-groovy-dsl-tactics-in-groovy-2/">put them together&lt;/a> to create a mini-language for health related studies. In this work, he needed to represent quantities of medecine, like 300 miligram of Chloroquinine, a drug used against Malaria. Groovy lets you add properties to numbers, and you can represent such quantities with just &lt;code>300.mg&lt;/code>. Inspired by this idea, the purpose of this article is to examine how to build a mini-DSL for manipulating measures and units by leveraging the &lt;a href="http://www.jscience.org/">JScience library&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>Domain-Specific Languages are a hot topic, and have been popularized by languages like <a href="http://groovy.codehaus.org/">Groovy</a> and Ruby thanks to their malleable syntax which make them a great fit for this purpose. In particular, <strong>Groovy allows you to create internal DSLs</strong>: business languages hosted by Groovy. In a recent research work, Tiago Antão has <a href="http://tiago.org/ps/2008/02/24/groovyscalarubypython-on-jvm/">decided to use Groovy</a> to <strong>model the resistance to drugs against the Malaria disease</strong>. In two blog posts, Tiago explains some of the <a href="http://tiago.org/ps/2008/02/25/dsl-tactics-in-groovy-1many/">tactics he used</a>, and how to <a href="http://tiago.org/ps/2008/02/27/chloroquine-malaria-treatment-and-groovy-dsl-tactics-in-groovy-2/">put them together</a> to create a mini-language for health related studies. In this work, he needed to represent quantities of medecine, like 300 miligram of Chloroquinine, a drug used against Malaria. Groovy lets you add properties to numbers, and you can represent such quantities with just <code>300.mg</code>. Inspired by this idea, the purpose of this article is to examine how to build a mini-DSL for manipulating measures and units by leveraging the <a href="http://www.jscience.org/">JScience library</a>.</p>
<p>First of all, let&rsquo;s speak about JScience. JScience is a Java library leveraging generics to represent various measurable quantities. JScience is also the Reference Implementation for <a href="http://www.jcp.org/en/jsr/detail?id=275">JSR-275: javax.measure.*</a>. Whether it is for measuring mass, length, time, amperes or volts (and many more), the calculations you can do are type-safe and checked at compile-time: you cannot add a second to a kilogram, your program wouldn&rsquo;t compile. This is definitely one of the strength of the library. However fluent the library is, the notation used to represent an amount of some unit is still not as readable as scientist could wish.</p>
<p>How do you represent a mass with JScience?</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import static</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">javax.measure.unit.SI.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">javax.measure.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">org.jscience.physics.amount.*</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#60a0b0;font-style:italic">// ...</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Amount<span style="color:#bbb"> </span>m3<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Amount.<span style="color:#4070a0">valueOf</span>(3,<span style="color:#bbb"> </span>KILO(GRAM));<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Amount<span style="color:#bbb"> </span>m2<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>Amount.<span style="color:#4070a0">valueOf</span>(<span style="color:#4070a0">&#34;2 kg&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>Amount<span style="color:#bbb"> </span>sum<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>m3.<span style="color:#4070a0">plus</span>(m2);<span style="color:#bbb">
</span></span></span></code></pre></div><p>The first expression leverages static imports to represent the <a href="http://jscience.org/api/javax/measure/unit/SI.html#KILO(javax.measure.unit.Unit)">KILO</a> (<a href="http://jscience.org/api/javax/measure/unit/SI.html#GRAM">GRAM</a>) unit, while the second simply parses the mass from a String. The last line does just an addition between the two masses. Still, it doesn&rsquo;t look like what a physicist would write. Wouldn&rsquo;t we want to use a mathematical notation, like <code>3 kg + 2 kg</code>? We will see how you can do this in Groovy.</p>
<p>Our first step will be to add units to numbers. We can&rsquo;t write <code>2 kg</code>, as it&rsquo;s not valid Groovy, instead, we&rsquo;ll write <code>2.kg</code>. To so, we&rsquo;ll add some dynamic properties to numbers, thanks to the <a href="http://groovy.codehaus.org/ExpandoMetaClass">ExpandoMetaClass</a>mechanism.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">javax.measure.unit.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.jscience.physics.amount.*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// Allow ExpandoMetaClass to traverse class hierarchies
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// That way, properties added to Number will also be available for Integer or BigDecimal, etc.
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>ExpandoMetaClass<span style="color:#666">.</span><span style="color:#4070a0">enableGlobally</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// transform number properties into an mount of a given unit represented by the property
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>Number<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">getProperty</span> <span style="color:#666">=</span> <span style="color:#666">{</span> String symbol <span style="color:#666">-&gt;</span> Amount<span style="color:#666">.</span><span style="color:#4070a0">valueOf</span><span style="color:#666">(</span>delegate<span style="color:#666">,</span> Unit<span style="color:#666">.</span><span style="color:#4070a0">valueOf</span><span style="color:#666">(</span>symbol<span style="color:#666">))</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// sample units
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>println<span style="color:#666">(</span> <span style="color:#40a070">2</span><span style="color:#666">.</span><span style="color:#4070a0">kg</span> <span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>println<span style="color:#666">(</span> <span style="color:#40a070">3</span><span style="color:#666">.</span><span style="color:#4070a0">m</span> <span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>println<span style="color:#666">(</span> <span style="color:#40a070">4.5</span><span style="color:#666">.</span><span style="color:#4070a0">in</span> <span style="color:#666">)</span>    
</span></span></code></pre></div><p>See how we created kilograms, meters and inches? The &ldquo;metaclass&rdquo; is what represents the runtime behavior of a class. When assigning a <a href="http://groovy.codehaus.org/Closures">closure</a> to the getProperty property, all the requests for properties on Numbers will be rooted to this closure. This closure then uses the JScience classes to create a Unit and an Amout. The delegate variable that you see in this closure represents the current number on which the properties are accessed.</p>
<p>Okay, fine, but at some point, you&rsquo;ll need to multiply these amounts by some factor, or you will want to add to lengths together. So we&rsquo;ll need to do leverage Groovy&rsquo;s <a href="http://groovy.codehaus.org/Operator+Overloading">operator overloading</a> to do some arithmetics. Whenever you have methods like multiply(), plus(), minus(), div(), or power(), Groovy will allow you to use the operators *, +, -, /, or **. Some of the conventions for certain of these operations being a bit different from those of Groovy, we have to add some new operator methods for certain of these operations:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// define opeartor overloading, as JScience doesn&#39;t use the same operation names as Groovy
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>Amount<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">multiply</span> <span style="color:#666">=</span> <span style="color:#666">{</span> Number factor <span style="color:#666">-&gt;</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">times</span><span style="color:#666">(</span>factor<span style="color:#666">)</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>Number<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">multiply</span> <span style="color:#666">=</span> <span style="color:#666">{</span> Amount amount <span style="color:#666">-&gt;</span> amount<span style="color:#666">.</span><span style="color:#4070a0">times</span><span style="color:#666">(</span>delegate<span style="color:#666">)</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>Number<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">div</span> <span style="color:#666">=</span> <span style="color:#666">{</span> Amount amount <span style="color:#666">-&gt;</span> amount<span style="color:#666">.</span><span style="color:#4070a0">inverse</span><span style="color:#666">().</span><span style="color:#4070a0">times</span><span style="color:#666">(</span>delegate<span style="color:#666">)</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>Amount<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">div</span> <span style="color:#666">=</span> <span style="color:#666">{</span> Number factor <span style="color:#666">-&gt;</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">divide</span><span style="color:#666">(</span>factor<span style="color:#666">)</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>Amount<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">div</span> <span style="color:#666">=</span> <span style="color:#666">{</span> Amount factor <span style="color:#666">-&gt;</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">divide</span><span style="color:#666">(</span>factor<span style="color:#666">)</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>Amount<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">power</span> <span style="color:#666">=</span> <span style="color:#666">{</span> Number factor <span style="color:#666">-&gt;</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">pow</span><span style="color:#666">(</span>factor<span style="color:#666">)</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>Amount<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">negative</span> <span style="color:#666">=</span> <span style="color:#666">{</span> <span style="color:#666">-&gt;</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">opposite</span><span style="color:#666">()</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// arithmetics: multiply, divide, addition, substraction, power
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>println<span style="color:#666">(</span> <span style="color:#40a070">18.4</span><span style="color:#666">.</span><span style="color:#4070a0">kg</span> <span style="color:#666">*</span> <span style="color:#40a070">2</span> <span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>println<span style="color:#666">(</span> <span style="color:#40a070">1800000</span><span style="color:#666">.</span><span style="color:#4070a0">kg</span> <span style="color:#4070a0">/ 3 )
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">println( 1.kg * 2 + 3.kg /</span> <span style="color:#40a070">4</span> <span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>println<span style="color:#666">(</span> <span style="color:#40a070">3</span><span style="color:#666">.</span><span style="color:#4070a0">cm</span> <span style="color:#666">+</span> <span style="color:#40a070">12</span><span style="color:#666">.</span><span style="color:#4070a0">m</span> <span style="color:#666">*</span> <span style="color:#40a070">3</span> <span style="color:#666">-</span> <span style="color:#40a070">1</span><span style="color:#666">.</span><span style="color:#4070a0">km</span> <span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>println<span style="color:#666">(</span> <span style="color:#40a070">1.5</span><span style="color:#666">.</span><span style="color:#4070a0">h</span> <span style="color:#666">+</span> <span style="color:#40a070">33</span><span style="color:#666">.</span><span style="color:#4070a0">s</span> <span style="color:#666">-</span> <span style="color:#40a070">12</span><span style="color:#666">.</span><span style="color:#4070a0">min</span> <span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>println<span style="color:#666">(</span> <span style="color:#40a070">30</span><span style="color:#666">.</span><span style="color:#4070a0">m</span><span style="color:#666">**</span><span style="color:#40a070">2</span> <span style="color:#666">-</span> <span style="color:#40a070">100</span><span style="color:#666">.</span><span style="color:#4070a0">ft</span><span style="color:#666">**</span><span style="color:#40a070">2</span> <span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// opposite and comparison
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>println<span style="color:#666">(</span> <span style="color:#666">-</span><span style="color:#40a070">3</span><span style="color:#666">.</span><span style="color:#4070a0">h</span> <span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>println<span style="color:#666">(</span> <span style="color:#40a070">3</span><span style="color:#666">.</span><span style="color:#4070a0">h</span> <span style="color:#666">&lt;</span> <span style="color:#40a070">4</span><span style="color:#666">.</span><span style="color:#4070a0">h</span> <span style="color:#666">)</span>
</span></span></code></pre></div><p>We can also do comparisons, as shown on the last line above, since these types are comparable. Again, free of charge. Something we have covered yet is compound units, such as speed, which is a mix of a distance and a duration. So, if you wanted to use a speed limit, you would like to write <code>90.km/h</code> but our DSL in its current state would only allow you to write <code>90.km/1.h</code>, which doesn&rsquo;t really look nice. To circumvent this issue, we could create as many variables as units. We could have a <code>h</code> variable, a <code>km</code> variable, etc. But I&rsquo;d prefer something more <em>automatic</em>, by letting the script itself provide these units. In Groovy scripts, you can have local variables (whenever you define a variable, it&rsquo;s a local variable), but you can also pass or access variables through a binding. This is a convenient way to pass data around when you integrate Groovy inside a Java application, for instance, to share a certain context of data. We are going to create a new Binding called UnitBinding which will override the <code>getVariable()</code> method, so that all non-local variables which are used withing the Groovy script are looked up in this binding. You&rsquo;ll notice a special treatment for the variable &lsquo;out&rsquo;, which is where the <code>println()</code> method looks for for the output stream to use.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// script binding to transform free standing unit reference like &#39;m&#39;, &#39;h&#39;, etc
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">UnitBinding</span> <span style="color:#007020;font-weight:bold">extends</span> Binding <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> <span style="color:#06287e">getVariable</span><span style="color:#666">(</span>String symbol<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>symbol <span style="color:#666">==</span> <span style="color:#4070a0">&#39;out&#39;</span><span style="color:#666">)</span> <span style="color:#007020;font-weight:bold">return</span> System<span style="color:#666">.</span><span style="color:#4070a0">out</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">return</span> Amount<span style="color:#666">.</span><span style="color:#4070a0">valueOf</span><span style="color:#666">(</span><span style="color:#40a070">1</span><span style="color:#666">,</span> Unit<span style="color:#666">.</span><span style="color:#4070a0">valueOf</span><span style="color:#666">(</span>symbol<span style="color:#666">))</span> 
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// use the script binding for retrieving unit references
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>binding <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> UnitBinding<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// inverse units
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>println<span style="color:#666">(</span> <span style="color:#40a070">30</span><span style="color:#666">.</span><span style="color:#4070a0">km</span><span style="color:#4070a0">/h + 2.m/</span>s <span style="color:#666">*</span> <span style="color:#40a070">2</span> <span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>println<span style="color:#666">(</span> <span style="color:#40a070">3</span> <span style="color:#666">*</span> <span style="color:#40a070">3</span><span style="color:#666">.</span><span style="color:#4070a0">mg</span><span style="color:#4070a0">/L )
</span></span></span><span style="display:flex;"><span><span style="color:#4070a0">println( 1/</span><span style="color:#40a070">2</span><span style="color:#666">.</span><span style="color:#4070a0">s</span> <span style="color:#666">-</span> <span style="color:#40a070">2</span><span style="color:#666">.</span><span style="color:#4070a0">Hz</span> <span style="color:#666">)</span>
</span></span></code></pre></div><p>The velocity now looks much more like the mathematical notation everybody would use. Now that all this magic is done, there&rsquo;s still one last thing we could do. Sometimes, you may want to convert different units, like feet and meters or inches and centimers. So, as the last step of our units DSL experiments, we&rsquo;ll add a <code>to()</code> method to do convertions.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// define to() method for unit conversion
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>Amount<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">to</span> <span style="color:#666">=</span> <span style="color:#666">{</span> Amount amount <span style="color:#666">-&gt;</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">to</span><span style="color:#666">(</span>amount<span style="color:#666">.</span><span style="color:#4070a0">unit</span><span style="color:#666">)</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// unit conversion
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>println<span style="color:#666">(</span> <span style="color:#40a070">200</span><span style="color:#666">.</span><span style="color:#4070a0">cm</span><span style="color:#666">.</span><span style="color:#4070a0">to</span><span style="color:#666">(</span>ft<span style="color:#666">)</span> <span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>println<span style="color:#666">(</span> <span style="color:#40a070">1</span><span style="color:#666">.</span><span style="color:#4070a0">in</span><span style="color:#666">.</span><span style="color:#4070a0">to</span><span style="color:#666">(</span>cm<span style="color:#666">)</span> <span style="color:#666">)</span>
</span></span></code></pre></div><p>At this point, we are able to easily manipulate amounts of any unit in a very convenient and natural notation. The <em>magic</em> trick of adding properties to numbers makes the process of creating a unit DSL straightforward. This DSL is just a small part of the equation, as you may certainly want to represent other business related concepts, but this article will have shown you how to decorate a powerful existing library, so that the code becomes more natural to use by the end users of your DSL. In further articles, we&rsquo;ll discover some other tricks! Stay tuned!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>JavaPolis interview with Guillaume Laforge, Groovy project lead</title><link>https://glaforge.dev/posts/2008/03/01/javapolis-interview-with-guillaume-laforge-groovy-project-lead/</link><pubDate>Sat, 01 Mar 2008 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2008/03/01/javapolis-interview-with-guillaume-laforge-groovy-project-lead/</guid><description>&lt;p>At &lt;a href="http://www.javapolis.com/confluence/display/JP07/Home">JavaPolis &amp;lsquo;07&lt;/a>, Dick Wall and Carl Quinn of &lt;a href="http://www.javaposse.com/">JavaPosse&lt;/a> fame have &lt;a href="http://parleys.com/display/PARLEYS/A+Groovy+interview+at+JavaPolis%2707?showComments=true">interviewed Guillaume Laforge&lt;/a>, the &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> project manager, and asked him about:&lt;/p>
&lt;ul>
&lt;li>the latest release of &lt;a href="http://www.infoq.com/articles/groovy-1.5-new">Groovy 1.5 and its novelties&lt;/a>,&lt;/li>
&lt;li>more details on the come back of the &amp;lsquo;infamous&amp;rsquo; for loop,&lt;/li>
&lt;li>the support of generics,&lt;/li>
&lt;li>the inclusion of closures in Java compared to Groovy&amp;rsquo;s closures,&lt;/li>
&lt;li>what Grails is and why it matters,&lt;/li>
&lt;li>how simple it is to leverage Grails in IT&amp;rsquo;s today infrastructure,&lt;/li>
&lt;li>the first class support of SQL and XML in Groovy,&lt;/li>
&lt;li>and also what the future will hold in the upcoming Groovy releases.&lt;/li>
&lt;/ul>
&lt;p>Go and view the &lt;a href="http://parleys.com/display/PARLEYS/A+Groovy+interview+at+JavaPolis%2707?showComments=true">interview on Parleys.com&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>At <a href="http://www.javapolis.com/confluence/display/JP07/Home">JavaPolis &lsquo;07</a>, Dick Wall and Carl Quinn of <a href="http://www.javaposse.com/">JavaPosse</a> fame have <a href="http://parleys.com/display/PARLEYS/A+Groovy+interview+at+JavaPolis%2707?showComments=true">interviewed Guillaume Laforge</a>, the <a href="http://groovy.codehaus.org/">Groovy</a> project manager, and asked him about:</p>
<ul>
<li>the latest release of <a href="http://www.infoq.com/articles/groovy-1.5-new">Groovy 1.5 and its novelties</a>,</li>
<li>more details on the come back of the &lsquo;infamous&rsquo; for loop,</li>
<li>the support of generics,</li>
<li>the inclusion of closures in Java compared to Groovy&rsquo;s closures,</li>
<li>what Grails is and why it matters,</li>
<li>how simple it is to leverage Grails in IT&rsquo;s today infrastructure,</li>
<li>the first class support of SQL and XML in Groovy,</li>
<li>and also what the future will hold in the upcoming Groovy releases.</li>
</ul>
<p>Go and view the <a href="http://parleys.com/display/PARLEYS/A+Groovy+interview+at+JavaPolis%2707?showComments=true">interview on Parleys.com</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Learn all about Groovy and Grails at the 2GX conference, Reston, VA</title><link>https://glaforge.dev/posts/2008/02/12/learn-all-about-groovy-and-grails-at-the-2gx-conference-reston-va/</link><pubDate>Tue, 12 Feb 2008 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2008/02/12/learn-all-about-groovy-and-grails-at-the-2gx-conference-reston-va/</guid><description>&lt;p>If you want to learn everything about Groovy and Grails, get in touch with all the projects committers and contributors, and have a great time, you should definitely not miss the &lt;a href="http://www.groovygrails.com/gg/2gexperience">Groovy / Grails Experience&lt;/a> that will take place in Reston, VA, USA &amp;ndash; February 21 - 23, 2008&lt;/p>
&lt;p>There&amp;rsquo;s a pretty impressive &lt;a href="http://www.groovygrails.com/gg/conference/speakers?showId=131">list of speakers&lt;/a>, and the &lt;a href="http://www.groovygrails.com/gg/conference/schedule?showId=131">session schedule&lt;/a> looks pretty tasty. It&amp;rsquo;s the best opportunity to get the right information straight from the horse&amp;rsquo;s mouth. A lot of topics will be covered, ranging from introductions to Groovy / Grails up to more advanced topics like DSLs, design patterns in dynamic languages, Java integration, Swing UIs, GORM, and more.&lt;/p></description><content:encoded>
<![CDATA[<p>If you want to learn everything about Groovy and Grails, get in touch with all the projects committers and contributors, and have a great time, you should definitely not miss the <a href="http://www.groovygrails.com/gg/2gexperience">Groovy / Grails Experience</a> that will take place in Reston, VA, USA &ndash; February 21 - 23, 2008</p>
<p>There&rsquo;s a pretty impressive <a href="http://www.groovygrails.com/gg/conference/speakers?showId=131">list of speakers</a>, and the <a href="http://www.groovygrails.com/gg/conference/schedule?showId=131">session schedule</a> looks pretty tasty. It&rsquo;s the best opportunity to get the right information straight from the horse&rsquo;s mouth. A lot of topics will be covered, ranging from introductions to Groovy / Grails up to more advanced topics like DSLs, design patterns in dynamic languages, Java integration, Swing UIs, GORM, and more.</p>
<p>The conference is on February 21st-23rd, so there&rsquo;s not much time left to get ready and register. But courtesy of <a href="http://www.g2one.com/">G2One</a>, the Groovy/Grails professional services company, and NFJS, if you register with the promo code <strong>2gx2008g2one</strong>, you&rsquo;ll even get a <strong>$150 discount</strong>!</p>
<p>Groovy and Grails have recently been coined as the technologies to learn in 2008, this conference will help you get started.</p>
<ul>
<li><a href="http://glaforge.free.fr/weblog/index.php?itemid=231">The future is now &ndash; Java development in 2008</a> by Andy Glover</li>
<li><a href="http://www.manageability.org/blog/stuff/five-java-technologies-to-learn-in-2008">Top Five Java Technologies to Learn in 2008</a> by Carlos Perez</li>
<li>The <a href="http://www.theserverside.com/news/thread.tss?thread_id=48270">debate on TheServerSide</a> about Carlos Perez&rsquo; post</li>
</ul>
<p>So with <a href="http://www.infoq.com/articles/groovy-1.5-new">Groovy 1.5</a> and <a href="http://www.theserverside.com/news/thread.tss?thread_id=48360">Grails 1.0 released</a> in the previous weeks and months, and the <a href="http://www.groovygrails.com/gg/2gexperience">2GX conference</a>, you&rsquo;re going to rock in 2008!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A Groovy kind of love</title><link>https://glaforge.dev/posts/2008/02/11/a-groovy-kind-of-love/</link><pubDate>Mon, 11 Feb 2008 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2008/02/11/a-groovy-kind-of-love/</guid><description>&lt;p>It&amp;rsquo;s funny, but before working on the &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> project, there was a song I&amp;rsquo;ve always liked: &lt;a href="http://www.lastfm.fr/music/Phil+Collins/_/A+Groovy+Kind+of+Love">A Groovy Kind of Love&lt;/a>, by Phill Collins. Do you think it was premonitory? That I&amp;rsquo;d work on Groovy? And speaking of Groovy love&amp;hellip; I&amp;rsquo;ve come across some nice love messages towards Groovy that I wanted to share with you.&lt;/p>
&lt;p>&lt;a href="http://jorgeonprogramming.blogspot.com/2008/02/why-i-like-groovy.html">Jorge Lugo&lt;/a>, a software engineer in the Washington DC area, sums up pretty well the appeal Groovy has to Java developers:&lt;/p></description><content:encoded>
<![CDATA[<p>It&rsquo;s funny, but before working on the <a href="http://groovy.codehaus.org/">Groovy</a> project, there was a song I&rsquo;ve always liked: <a href="http://www.lastfm.fr/music/Phil+Collins/_/A+Groovy+Kind+of+Love">A Groovy Kind of Love</a>, by Phill Collins. Do you think it was premonitory? That I&rsquo;d work on Groovy? And speaking of Groovy love&hellip; I&rsquo;ve come across some nice love messages towards Groovy that I wanted to share with you.</p>
<p><a href="http://jorgeonprogramming.blogspot.com/2008/02/why-i-like-groovy.html">Jorge Lugo</a>, a software engineer in the Washington DC area, sums up pretty well the appeal Groovy has to Java developers:</p>
<blockquote>
<p><strong>Why I like Groovy</strong><br />
I became interested in Groovy recently when I discovered that it is like Ruby, but is meant for the Java language and platform. Groovy provides Ruby-like features with out having to abandon the Java language, frameworks and tools that I&rsquo;ve been using for years.<br />
[&hellip;]<br />
In Conclusion, Groovy is a great language. I hope to use it more often in the future.</p></blockquote>
<p>I hope Jorge will attend the <a href="http://www.groovygrails.com/gg/2gexperience">2GX conference</a>, as it&rsquo;ll be a tremendous opportunity to dive into Groovy and <a href="http://grails.org/">Grails</a>!</p>
<p>Rob Breidecker, who lives in St-Louis, MO, USA, is <a href="http://evolutionarygoo.com/blog/?p=162">learning Groovy</a> and seems to pretty much like what he&rsquo;s learned so far:</p>
<blockquote>
<p><strong>Giving Groovy a Chance</strong><br />
Last year I learned Ruby and Rails and found them to be fun to program in and also very productive. I was in particular very impressed with how well thought out Rails was. After years of programming Web applications I could see the power of automating many of the unpleasant and mundane tasks that go along with Web development.<br />
Since Java is still my bread and butter, I am spending time this year learning Groovy and eventually hope to start learning Grails. So far I like what I have seen with Groovy. It shares many of the language features of Ruby and is very easy to install (especially if you already have a JVM installed on your machine). I also get to use all of the existing Java classes I am already familiar with, so I am able to be productive with Groovy right out of the gate. Is there anyone out there playing with Groovy and would like to share what kinds of real world things are you doing with it?</p></blockquote>
<p>For Java developers, it&rsquo;s obviously way easier to learn a language like Groovy with its Java-like syntax, and because it&rsquo;s leveraging all the APIs, tools, frameworks you&rsquo;ve learned to master over the time. Same for Grails, with its Spring and Hibernate background. <strong>Betting on Groovy and Grails is the best way to protect your investment</strong>.</p>
<p>And probably the best and funniest love message at the end, mostly in time for the Valentine&rsquo;s day, by <a href="http://www.jroller.com/orrego/entry/i_am_married_to_java">Carlos Orrego</a>:</p>
<blockquote>
<p><strong>I am married to Java, but i have a lover&hellip; Groovy</strong><br />
Java has been there for me many years. Solid, stable, reliable, mature, old&hellip; I know java well, i think she knows me too.<br />
Then i met Groovy. Young, sexy, dynamic, agile&hellip; I am getting to know Groovy, i am getting butterflies in my stomach again.<br />
The best things is that they get along just fine. They even talk together in harmony.<br />
What else can i say?</p></blockquote>
<p>That&rsquo;s a Groovy kind of love!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>The Groovy Zone, community news site for the Groovy and Grails developers</title><link>https://glaforge.dev/posts/2008/01/28/the-groovy-zone-community-news-site-for-the-groovy-and-grails-developers/</link><pubDate>Mon, 28 Jan 2008 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2008/01/28/the-groovy-zone-community-news-site-for-the-groovy-and-grails-developers/</guid><description>&lt;p>A new source of information on &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> and &lt;a href="http://grails.org/">Grails&lt;/a> has just been launched: the &lt;a href="http://groovy.dzone.com/">Groovy Zone&lt;/a>. Rick Ross et al. have built upon the &lt;a href="http://www.dzone.com/">DZone&lt;/a> community-driven linkblog, and &lt;a href="http://www.javalobby.com/">JavaLobby&lt;/a>, to create various &amp;ldquo;Zones&amp;rdquo; on numerous topics like Java, AJAX, CSS, and also Groovy and Grails. &lt;a href="http://www.jroller.com/aalmiray/">Andres Almiray&lt;/a>, &lt;a href="http://glaforge.free.fr/weblog/index.php?itemid=229">Steven Devijver&lt;/a> and myself have become &amp;ldquo;zone leaders&amp;rdquo;, to shepherd the Groovy and Grails content.&lt;/p>
&lt;p>&lt;a href="http://groovy.dzone.com/">&lt;figure>
&lt;a href="#img-0ace806b18b165d1644a6b7c83a8e0a9">
&lt;img src="http://glaforge.free.fr/weblog/media/GroovyZoneLogo.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-0ace806b18b165d1644a6b7c83a8e0a9">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="http://glaforge.free.fr/weblog/media/GroovyZoneLogo.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/a>&lt;/p>
&lt;p>We had &lt;a href="http://www.aboutgroovy.com/">AboutGroovy&lt;/a> where Scott Davis lists on the interesting news in the Groovy and Grails spheres, and Glen Smith&amp;rsquo;s&lt;a href="http://www.groovyblogs.org/">GroovyBlogs&lt;/a> blog aggrgator listing several super interesting blogs related to Groovy and Grails&amp;hellip; and now we have the GroovyZone!&lt;/p></description><content:encoded>
<![CDATA[<p>A new source of information on <a href="http://groovy.codehaus.org/">Groovy</a> and <a href="http://grails.org/">Grails</a> has just been launched: the <a href="http://groovy.dzone.com/">Groovy Zone</a>. Rick Ross et al. have built upon the <a href="http://www.dzone.com/">DZone</a> community-driven linkblog, and <a href="http://www.javalobby.com/">JavaLobby</a>, to create various &ldquo;Zones&rdquo; on numerous topics like Java, AJAX, CSS, and also Groovy and Grails. <a href="http://www.jroller.com/aalmiray/">Andres Almiray</a>, <a href="http://glaforge.free.fr/weblog/index.php?itemid=229">Steven Devijver</a> and myself have become &ldquo;zone leaders&rdquo;, to shepherd the Groovy and Grails content.</p>
<p><a href="http://groovy.dzone.com/"><figure>
  <a href="#img-0ace806b18b165d1644a6b7c83a8e0a9">
    <img src="http://glaforge.free.fr/weblog/media/GroovyZoneLogo.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-0ace806b18b165d1644a6b7c83a8e0a9">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="http://glaforge.free.fr/weblog/media/GroovyZoneLogo.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</a></p>
<p>We had <a href="http://www.aboutgroovy.com/">AboutGroovy</a> where Scott Davis lists on the interesting news in the Groovy and Grails spheres, and Glen Smith&rsquo;s<a href="http://www.groovyblogs.org/">GroovyBlogs</a> blog aggrgator listing several super interesting blogs related to Groovy and Grails&hellip; and now we have the GroovyZone!</p>
<p>I wrote a first little article showing how to <a href="http://groovy.dzone.com/news/java-groovy-few-easy-steps">transition your Java code into Groovy</a>, showing Groovy&rsquo;s Java-superset nature while learning a new Groovy feature one at a time. I stole the idea from Andres and Paul who used these kind of samples in their Groovy conference presentations &ndash; give to Caesar what is Caesar&rsquo;s! As time permits, I&rsquo;ll continue showing some simple examples, to help Java developers get up to speed with Groovy, to make their Java code more Groovy, and to learn new features in each article.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>MarkMail archives the Groovy mailing-lists and shows their success</title><link>https://glaforge.dev/posts/2008/01/23/markmail-archives-the-groovy-mailing-lists-and-shows-their-success/</link><pubDate>Wed, 23 Jan 2008 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2008/01/23/markmail-archives-the-groovy-mailing-lists-and-shows-their-success/</guid><description>&lt;p>Jason Hunter from &lt;a href="http://www.marklogic.com/">MarkLogic&lt;/a> (and &lt;a href="http://www.jdom.org/">JDOM&lt;/a> fame) has crawled the &lt;a href="http://groovy.markmail.org/">Groovy mailing-lists archives&lt;/a> with his gorgeous &lt;a href="http://markmail.org/">MarkMail&lt;/a> interface.&lt;/p>
&lt;p>Not all the archives have been indexed so far, as we&amp;rsquo;re still missing some older archives from before April 2004, but there&amp;rsquo;s already about 70K messages loaded. In the following screenshot, you can see the number of messages sent to the lists per month, across the course of time.&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-0e290b9aba322b8eb47da6a553fa1eff">
&lt;img src="https://glaforge.dev/img/markmail/markmail-list-graph.png"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-0e290b9aba322b8eb47da6a553fa1eff">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/markmail/markmail-list-graph.png"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>An interesting remark: you&amp;rsquo;ll certainly have noticed the jump in the traffic around January 2007. It coincides with the release of Groovy 1.0, our first official final and stable release, after years of betas. Almost overnight (or &amp;ldquo;over-month&amp;rdquo; shall I say), the traffic on the lists doubled!&lt;/p></description><content:encoded>
<![CDATA[<p>Jason Hunter from <a href="http://www.marklogic.com/">MarkLogic</a> (and <a href="http://www.jdom.org/">JDOM</a> fame) has crawled the <a href="http://groovy.markmail.org/">Groovy mailing-lists archives</a> with his gorgeous <a href="http://markmail.org/">MarkMail</a> interface.</p>
<p>Not all the archives have been indexed so far, as we&rsquo;re still missing some older archives from before April 2004, but there&rsquo;s already about 70K messages loaded. In the following screenshot, you can see the number of messages sent to the lists per month, across the course of time.</p>
<p><figure>
  <a href="#img-0e290b9aba322b8eb47da6a553fa1eff">
    <img src="/img/markmail/markmail-list-graph.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-0e290b9aba322b8eb47da6a553fa1eff">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/markmail/markmail-list-graph.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>An interesting remark: you&rsquo;ll certainly have noticed the jump in the traffic around January 2007. It coincides with the release of Groovy 1.0, our first official final and stable release, after years of betas. Almost overnight (or &ldquo;over-month&rdquo; shall I say), the traffic on the lists doubled!</p>
<p>MarkMail makes clever use of JavaScript to make some nice horizontal scrolling animations to go from the list view, to the email view.</p>
<p><figure>
  <a href="#img-c22261d47439628b823d071bbcb08998">
    <img src="/img/markmail/markmail-list-view.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-c22261d47439628b823d071bbcb08998">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/markmail/markmail-list-view.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>The email view with thread grouping, etc:</p>
<p><figure>
  <a href="#img-711853232fbfab3ef3b8e2e3860bff5a">
    <img src="/img/markmail/markmail-list-email-view.png"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-711853232fbfab3ef3b8e2e3860bff5a">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/markmail/markmail-list-email-view.png"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Although the mailing-list archives are also available on <a href="http://dir.gmane.org/search.php?match=groovy">Gmane</a> and <a href="http://www.nabble.com/codehaus---Groovy-f11866.html">Nabble</a>, I also encourage you to try the <a href="http://groovy.markmail.org/">new MarkMail interface</a>!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy not Enterprise-ready, you're kidding?</title><link>https://glaforge.dev/posts/2008/01/19/groovy-not-enterprise-ready-you-re-kidding/</link><pubDate>Sat, 19 Jan 2008 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2008/01/19/groovy-not-enterprise-ready-you-re-kidding/</guid><description>&lt;p>Graeme pointed me at a &lt;a href="http://www.huxili.com/reports/ID000188">white-paperish article&lt;/a> claiming Groovy would not be Enterprise-ready. If the article had been acurate, I would have welcomed it, and we could have found ways to improve Groovy to make it ready, but unfortunately, the author did not do his job properly, and only spread FUD by saying Groovy doesn&amp;rsquo;t hold to its promises.&lt;/p>
&lt;p>As this article is pretty thin on the technical aspects, I won&amp;rsquo;t explain why his conclusions are wrong &amp;ndash; and also because &lt;a href="http://graemerocher.blogspot.com/2008/01/re-groovy-and-jruby-enterprise-ready.html">Graeme explained this already&lt;/a>. Instead, I&amp;rsquo;ll just comment on a few of his claims.&lt;/p></description><content:encoded>
<![CDATA[<p>Graeme pointed me at a <a href="http://www.huxili.com/reports/ID000188">white-paperish article</a> claiming Groovy would not be Enterprise-ready. If the article had been acurate, I would have welcomed it, and we could have found ways to improve Groovy to make it ready, but unfortunately, the author did not do his job properly, and only spread FUD by saying Groovy doesn&rsquo;t hold to its promises.</p>
<p>As this article is pretty thin on the technical aspects, I won&rsquo;t explain why his conclusions are wrong &ndash; and also because <a href="http://graemerocher.blogspot.com/2008/01/re-groovy-and-jruby-enterprise-ready.html">Graeme explained this already</a>. Instead, I&rsquo;ll just comment on a few of his claims.</p>
<blockquote>
<p>Some even claims that Java should be replaced by their preferred dynamic language.</p></blockquote>
<p>I don&rsquo;t know who &ldquo;some&rdquo; is in this sentence, but at least, that&rsquo;s not me. Developers should always have the choice: the best tool for the job. <a href="http://groovy.codehaus.org/">Groovy</a> has initially been conceived to complement Java, not to replace it &ndash; which doesn&rsquo;t mean that you can&rsquo;t build a full project using Groovy only, as demonstrated by the powerful <a href="http://grails.org/">Grails framework</a>. Java&rsquo;s a great language, and a wonderful platform. Unlike other<a href="http://davidvancouvering.blogspot.com/2008/01/say-it-aint-so-ruby-on-rails-not-thread.html">platforms</a>, Java&rsquo;s threadsafe, it optimizes access to databases, it scales extraordinarily well, etc.</p>
<blockquote>
<p>SAP, one of the largest providers of enterprise software and services in the world, have released a new community driven product based on Groovy.</p></blockquote>
<p>Yes, SAP, among other big vendors (IBM, Oracle, JBoss) who did the same, has decided to use <a href="http://groovy.codehaus.org/">Groovy</a> and <a href="http://grails.org/">Grails</a> for its new innovative project. Do you think a company like SAP would have chosen Groovy and Grails if these technologies would not have been Enterprise-ready? See a couple of links on this topic:</p>
<ul>
<li>A <a href="http://graemerocher.blogspot.com/2007/12/sap-announces-composition-on-grails-10.html">testimonial on Graeme&rsquo;s blog</a></li>
<li>And the <a href="https://www.sdn.sap.com/irj/sdn/wiki?path=/display/Community/Composition+on+Grails">project itself</a></li>
</ul>
<blockquote>
<p>On the JRuby side, it has solid backing from Sun Microsystems and has been integrated into NetBeans IDE</p></blockquote>
<p>Right, Sun&rsquo;s been investing in JRuby by hiring the two lead developers. They&rsquo;ve been putting a lot of marketing effort on this, to surf the hype wave of Ruby on Rails. But <strong>Sun has also helped the Groovy project</strong> by giving us access to a nice Sun Fire T2000 machine for our performance testing needs, they&rsquo;ve let us create a JSR for standardizing Groovy in the Java Community Process, and they are also currently working on <a href="http://martin.adamek.sk/">Groovy and Grails support in NetBeans</a>. There&rsquo;s just less marketing money spent on Groovy and Grails.</p>
<blockquote>
<p>Before applying Groovy or JRuby to your enterprise applications, their suitability and reliability should be evaluated seriously.</p></blockquote>
<p>Very true, and you should definitely not just trust anything you read on the web. You have to do your own homework, and evaluate the technical solution in a realistic context, rather than believing in some micro-benchmark or amateurish white-paper.</p>
<blockquote>
<p>Groovy and JRuby are still very young and has not yet been confirmed in production.</p></blockquote>
<p>For JRuby, I can&rsquo;t comment, despite the fact I haven&rsquo;t heard of much well-known JRuby-powered systems in production, but hopefully, someone can comment on this blog to get this straight. But at least, for Groovy, I personally know quite a few mission-critical applications that already embed Groovy. I can even tell you <strong>Groovy&rsquo;s handling million-dollars hedge funds daily in a Londonian financial institution</strong>. I can also tell you that US <strong>Fortune 500 insurance company Mutual of Omaha is running a risk calculation engine with thousands of lines of Groovy business rules</strong>. And what about the<strong>US National Cancer Institute which also uses Groovy to validate patients file details</strong>? And one of the <strong>top major American credit card company</strong> (in the top 20 on Fortune 500) that also uses Groovy in production? And Groovy would not be Enterprise-ready?</p>
<blockquote>
<p>First, we observed that the Groovy runtime is not stable, that is, memory consumption increased linearly with repeating times. In other words, a certain amount of memory is lost - i.e. memory leakage - during each evaluation of the script. This is a serious flaw of the Groovy runtime; Groovy can not be used in long-running server environment unless alternative solutions can be found to avoid memory leakage. The simplest solution is &ldquo;runtime recycling&rdquo; &ndash; throwing away the Groovy runtime after each execution and let JVM recycle Groovy runtime memory.</p></blockquote>
<p>There is <strong>no leakage of memory in Groovy</strong> at all. Each time you evaluate a script at runtime, a new class is created in memory. And the classloader just remembers this class, in case it may be used further on down the road, instead of bearing the cost of recompiling / reevaluating it each time. However, if you wanted to just evaluate it once, discard the classloader or the Groovy shell used afterwards. It&rsquo;s as simple as that. No memory leaked at all. Just use the APIs as they are intended to be used, and don&rsquo;t claim that they are flawed because you didn&rsquo;t understand how to use them properly.</p>
<blockquote>
<p>In certain server environments the current solution by recycling Groovy runtime is not enough and you may still have memory leakage problem. This is because certain servers always keep the classes in memory even they are no longer required. One of the example is the JBoss application server. In JBoss, classes are not unloaded from the memory even they are not required by the system. Of course, this is a design problem of JBoss and may be changed in future versions.</p></blockquote>
<p>I don&rsquo;t know the JBoss internals enough to comment on this one, but as the Groovy classes are loaded by the Groovy classloader itself, I suspect the JBoss infrastructure would just let the classloader to be garbage collected otherwise it would be mostly impossible to make serious applications work on JBoss AS, which I highly doubt. Hopefully, someone with more JBoss knowledge can better comment on this.</p>
<p>Groovy has been <strong>very stable and mature for a long time already</strong>. It is being <strong>used by many high-profile companies and institutions throughout the world with great success</strong>. It is a pity to see such FUD spread of Groovy by an amateurish white-paper written by someone who doesn&rsquo;t seem to have done his researches well.</p>
<p><strong>Update #1:</strong> I&rsquo;ve added a new Enterprise use case for Groovy, where one of the US Fortune 20 credit card / bank company leverages Groovy, as someone pointed me at in a mail.</p>
<p><strong>Update #2:</strong> Steven made a <a href="http://groovy.dzone.com/news/groovyshell-and-memory-leaks">nice technical analysis on groovy.dzone.com</a>, so if you want to learn more about the technical aspects of the story, and better understand the memory and classloading behavior in Groovy, go right there.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>What's new in Groovy 1.5</title><link>https://glaforge.dev/posts/2007/12/09/whats-new-in-groovy-15/</link><pubDate>Sun, 09 Dec 2007 16:00:40 +0100</pubDate><guid>https://glaforge.dev/posts/2007/12/09/whats-new-in-groovy-15/</guid><description>&lt;p>&lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a>, the Java-like dynamic language for the JVM, matures over time like good wines. After the successful release of Groovy 1.0 in January 2007, the next major milestone with the 1.5 label already hits the shelves. With it, come several interesting novelties that we will examine in this article. The major addition to the language is the support of Java 5 features with annotations, generics and enums, making Groovy &lt;strong>the sole alternative dynamic language for the JVM fully supporting frameworks like Spring, Hibernate, JPA&lt;/strong>, Google Guice, or TestNG. Apart from the new Java 5 features, a few syntax enhancements find their way in the language, as well as a more powerful dynamic behavior customization, a Swing UI builder on steroids, and improved tooling support.&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://groovy.codehaus.org/">Groovy</a>, the Java-like dynamic language for the JVM, matures over time like good wines. After the successful release of Groovy 1.0 in January 2007, the next major milestone with the 1.5 label already hits the shelves. With it, come several interesting novelties that we will examine in this article. The major addition to the language is the support of Java 5 features with annotations, generics and enums, making Groovy <strong>the sole alternative dynamic language for the JVM fully supporting frameworks like Spring, Hibernate, JPA</strong>, Google Guice, or TestNG. Apart from the new Java 5 features, a few syntax enhancements find their way in the language, as well as a more powerful dynamic behavior customization, a Swing UI builder on steroids, and improved tooling support.</p>
<h2 id="a-groovier-groovy-and-why-it-matters">A groovier Groovy and why it matters</h2>
<p>Groovy&rsquo;s key selling point has always been its <strong>seamless integration with Java</strong>. You can mix and match Groovy and Java classes together in very easy ways: You may have a Groovy class extending a Java class implementing a Groovy interface, and vice versa. Most of the other alternative JVM languages unfortunately won&rsquo;t let you seamlessly interchange classes of two different languages. So if you want to use the best language for the job without compromising your nice class hierarchy, you won&rsquo;t have many options to choose from, and Groovy gives you all the freedom for integrating both languages in the most transparent way.</p>
<p>Groovy shares the same libraries, the same object model, the same threading model, and the same security model as Java. In a way, you can consider Groovy as an implementation detail of your Java project, <strong>without having to bear problematic impedance mismatch issues</strong>.</p>
<p>Groovy is Java, and Groovy makes Java groovier. Compared with other languages, Groovy is certainly the language that provides the <strong>flattest learning curve to Java developers</strong>, thanks to a very similar syntax.</p>
<p>This is even more important to keep in mind that Groovy generates normal Java bytecode and uses the usual JDK libraries, so you won&rsquo;t need to learn whole new APIs or have complex integration mechanisms: out of the box, Groovy and Java are interchangeable. The added benefit is that you can <strong>protect the investment</strong> you made in Java <strong>skills for your developers</strong>, or in <strong>costly application servers</strong>, or third party or <strong>home-grown libraries</strong>, as you can reuse all of them without a problem from Groovy.</p>
<p>Speaking of calling into JDK, third party, or in-house libraries, alternative languages which don&rsquo;t support strong typing, can&rsquo;t always call all Java methods because of the fact they can&rsquo;t choose a certain polymorphic variation of the same method. When choosing an alternative language to improve your productivity or make your code more readable, if you need to call other Java classes, you will have to be very careful to the choice of language you will make, as you may encounter some road blocks along the way.</p>
<p>These days, all major Enterprise frameworks require the use of features like annotations, enums or generics, to be leveraged at their fullest extent. Fortunately, with Groovy 1.5, developers can benefit from the support of all these Java 5 features in their projects. Let&rsquo;s see how annotations, enums and generics can be used from Groovy.</p>
<h2 id="java-5-additions">Java 5 additions</h2>
<p>The Groovy compiler always generates Java bytecode which is compatible with older Java VMs, but relies on JDK 1.4 for the core libraries it is using. However, for certain of these Java 5 additions, using Java 5 bytecode was required, so that, for example, the generated classes may contain the bytecode information representing annotations with runtime retention policy. So although Groovy 1.5 can run on JDK 1.4, certain of these features will only be usable on JDK 5 - when this is the case, it will be mentioned in this article.</p>
<h3 id="variable-arguments">Variable arguments</h3>
<p>In Java 5, the ellipsis notation was created to denote methods with variable length arguments. With those little triple dots, Java lets users put as many parameters of the same type at the end of a method - actually, the vararg parameter is just an array of elements of that type. Varargs were already present in Groovy 1.0 - and still work with a JDK 1.4 runtime, but it is good to show how you can use them. Basically, whenever the last argument of a method is an array of objects, or a parameter declaration with a triple dot, you can pass multiple parameters to this method.</p>
<p>A first example will show the usage of varargs in Groovy with the ellipsis:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">int</span> <span style="color:#06287e">sum</span><span style="color:#666">(</span><span style="color:#902000">int</span><span style="color:#666">...</span> someInts<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> total <span style="color:#666">=</span> <span style="color:#40a070">0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">for</span> <span style="color:#666">(</span><span style="color:#902000">int</span> i <span style="color:#666">=</span> <span style="color:#40a070">0</span><span style="color:#666">;</span> i <span style="color:#666">&lt;</span> someInts<span style="color:#666">.</span><span style="color:#4070a0">size</span><span style="color:#666">();</span> i<span style="color:#666">++)</span>
</span></span><span style="display:flex;"><span>        total <span style="color:#666">+=</span> someInts<span style="color:#666">[</span>i<span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">return</span> total
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">sum</span><span style="color:#666">(</span><span style="color:#40a070">1</span><span style="color:#666">)</span>       <span style="color:#666">==</span> <span style="color:#40a070">1</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">sum</span><span style="color:#666">(</span><span style="color:#40a070">1</span><span style="color:#666">,</span> <span style="color:#40a070">2</span><span style="color:#666">)</span>    <span style="color:#666">==</span> <span style="color:#40a070">3</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">sum</span><span style="color:#666">(</span><span style="color:#40a070">1</span><span style="color:#666">,</span> <span style="color:#40a070">2</span><span style="color:#666">,</span> <span style="color:#40a070">3</span><span style="color:#666">)</span> <span style="color:#666">==</span> <span style="color:#40a070">6</span>
</span></span></code></pre></div><p>The assertions used in this example show how we can pass as many ints as desired. It is also interesting to see that, for more syntax compatibility with Java, the classical for loop has been added into Groovy - despite the presence of the groovier version with the in keyword that can also transparently iterate over various array or collection types.</p>
<p>Note that it is possible to have varargs support even with an array as last parameter by declaring the method as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">int</span> <span style="color:#06287e">sum</span><span style="color:#666">(</span><span style="color:#902000">int</span><span style="color:#666">[]</span> someInts<span style="color:#666">)</span> <span style="color:#666">{</span> <span style="color:#60a0b0;font-style:italic">/* */</span> <span style="color:#666">}</span>
</span></span></code></pre></div><p>This snippet is really trivial, and there are obviously more expressive ways of calculating a sum. For instance, if you have a list of numbers, you can sum all of them in a single line of code:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#666">[</span><span style="color:#40a070">1</span><span style="color:#666">,</span> <span style="color:#40a070">2</span><span style="color:#666">,</span> <span style="color:#40a070">3</span><span style="color:#666">].</span><span style="color:#4070a0">sum</span><span style="color:#666">()</span> <span style="color:#666">==</span> <span style="color:#40a070">6</span>
</span></span></code></pre></div><p>Varargs in Groovy don&rsquo;t require JDK 5 as the underlying Java runtime, unlike annotations that we are now going to look at in the following section.</p>
<h3 id="annotations">Annotations</h3>
<p>As shown in the <a href="http://docs.jboss.com/seam/1.3.0.ALPHA/reference/en/html/ch10.html">documentation of JBoss Seam</a> which supports Groovy for writing its entities, controllers and components, annotations like <code>@Entity</code>, <code>@Id</code>, <code>@Override</code> and others can be used to decorate your beans:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Entity</span>
</span></span><span style="display:flex;"><span><span style="color:#555;font-weight:bold">@Name</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;hotel&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Hotel</span> <span style="color:#007020;font-weight:bold">implements</span> Serializable
</span></span><span style="display:flex;"><span><span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>     <span style="color:#555;font-weight:bold">@Id</span> <span style="color:#555;font-weight:bold">@GeneratedValue</span>
</span></span><span style="display:flex;"><span>     Long id
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>     <span style="color:#555;font-weight:bold">@Length</span><span style="color:#666">(</span>max<span style="color:#666">=</span><span style="color:#40a070">50</span><span style="color:#666">)</span> <span style="color:#555;font-weight:bold">@NotNull</span>
</span></span><span style="display:flex;"><span>     String name
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>     <span style="color:#555;font-weight:bold">@Length</span><span style="color:#666">(</span>max<span style="color:#666">=</span><span style="color:#40a070">100</span><span style="color:#666">)</span> <span style="color:#555;font-weight:bold">@NotNull</span>
</span></span><span style="display:flex;"><span>     String address
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>     <span style="color:#555;font-weight:bold">@Length</span><span style="color:#666">(</span>max<span style="color:#666">=</span><span style="color:#40a070">40</span><span style="color:#666">)</span> <span style="color:#555;font-weight:bold">@NotNull</span>
</span></span><span style="display:flex;"><span>     String city
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>     <span style="color:#555;font-weight:bold">@Length</span><span style="color:#666">(</span>min<span style="color:#666">=</span><span style="color:#40a070">2</span><span style="color:#666">,</span> max<span style="color:#666">=</span><span style="color:#40a070">10</span><span style="color:#666">)</span> <span style="color:#555;font-weight:bold">@NotNull</span>
</span></span><span style="display:flex;"><span>     String state
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>     <span style="color:#555;font-weight:bold">@Length</span><span style="color:#666">(</span>min<span style="color:#666">=</span><span style="color:#40a070">4</span><span style="color:#666">,</span> max<span style="color:#666">=</span><span style="color:#40a070">6</span><span style="color:#666">)</span> <span style="color:#555;font-weight:bold">@NotNull</span>
</span></span><span style="display:flex;"><span>     String zip
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>     <span style="color:#555;font-weight:bold">@Length</span><span style="color:#666">(</span>min<span style="color:#666">=</span><span style="color:#40a070">2</span><span style="color:#666">,</span> max<span style="color:#666">=</span><span style="color:#40a070">40</span><span style="color:#666">)</span> <span style="color:#555;font-weight:bold">@NotNull</span>
</span></span><span style="display:flex;"><span>     String country
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>     <span style="color:#555;font-weight:bold">@Column</span><span style="color:#666">(</span>precision<span style="color:#666">=</span><span style="color:#40a070">6</span><span style="color:#666">,</span> scale<span style="color:#666">=</span><span style="color:#40a070">2</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>     BigDecimal price
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>     <span style="color:#555;font-weight:bold">@Override</span>
</span></span><span style="display:flex;"><span>     String <span style="color:#06287e">toString</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>         <span style="color:#007020;font-weight:bold">return</span> <span style="color:#4070a0">&#34;Hotel(${name}, ${address}, ${city}, ${zip})&#34;</span>
</span></span><span style="display:flex;"><span>     <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>The Hotel entity is marked with the @Entity annotation, and it&rsquo;s given a name through @Name. Different parameters can be passed to your annotations like in the @Length annotation constraint where different upper and lower bound can be set for validation purpose. You can also notice <strong>Groovy properties</strong> in action: Where are all the getters and setters? Where are the public or private modifiers? You don&rsquo;t have to wait for Java 7 or 8 to get properties! By convention, defining a property is as simple as String country: a private country field will be auto-generated, as well as a public getter and setter. <strong>Your code becomes naturally more concise and readable</strong>.</p>
<p>Annotations can be used on classes, fields, methods and method parameters, like in Java. There are, however, two gotchas to be aware of. Firstly, you can use annotations in Groovy, but you cannot yet define them - however, it will be possible in an upcoming version of Groovy. Secondly, although the syntax is almost 100% the same as in Java, there is a little difference when an array of values is passed in parameter of the annotation: instead of curly braces to surround the elements, Groovy requires the use of square brackets to offer a more homogeneous syntax - since Groovy lists and arrays use square brackets to surround their elements as well.</p>
<p>With annotations in Groovy 1.5, you can easily define your <a href="http://www.curious-creature.org/2007/03/25/persistence-made-easy-with-groovy-and-jpa/">JPA or Hibernate annotated beans</a> in Groovy, add an @Transactional annotation on your Spring services, test your Swing UI with TestNG and <a href="http://www.jroller.com/aalmiray/entry/testing_groovy_uis_with_fest">Fest</a>. All the useful and powerful enterprise frameworks leveraging annotations can be used from your Groovy-powered projects.</p>
<h3 id="enums">Enums</h3>
<p>Whenever you need a fixed set of constants of a same type, Enums come in handy. Say you need a clean way to define constants for days without resorting to using integer constants? Then Enums are your friend. The following snippet shows how to define the days of the week:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">enum</span> <span style="color:#0e84b5;font-weight:bold">Day</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>     SUNDAY<span style="color:#666">,</span> MONDAY<span style="color:#666">,</span> TUESDAY<span style="color:#666">,</span> WEDNESDAY<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>     THURSDAY<span style="color:#666">,</span> FRIDAY<span style="color:#666">,</span> SATURDAY
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Once you have defined your enum, you can use it as in Java with the usual notation <code>Day.MONDAY</code> and you can spice up your <code>switch</code> / <code>case</code> statements with it as well:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> today <span style="color:#666">=</span> Day<span style="color:#666">.</span><span style="color:#4070a0">SATURDAY</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">switch</span> <span style="color:#666">(</span>today<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>     <span style="color:#60a0b0;font-style:italic">// Saturday or Sunday
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>     <span style="color:#007020;font-weight:bold">case</span> <span style="color:#666">[</span>Day<span style="color:#666">.</span><span style="color:#4070a0">SATURDAY</span><span style="color:#666">,</span> Day<span style="color:#666">.</span><span style="color:#4070a0">SUNDAY</span><span style="color:#666">]:</span>
</span></span><span style="display:flex;"><span>         println <span style="color:#4070a0">&#34;Weekends are cool&#34;</span>
</span></span><span style="display:flex;"><span>         <span style="color:#007020;font-weight:bold">break</span>
</span></span><span style="display:flex;"><span>     <span style="color:#60a0b0;font-style:italic">// a day between Monday and Friday
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>     <span style="color:#007020;font-weight:bold">case</span> Day<span style="color:#666">.</span><span style="color:#4070a0">MONDAY</span><span style="color:#666">..</span><span style="color:#4070a0">Day</span><span style="color:#666">.</span><span style="color:#4070a0">FRIDAY</span><span style="color:#666">:</span>
</span></span><span style="display:flex;"><span>         println <span style="color:#4070a0">&#34;Boring work day&#34;</span>
</span></span><span style="display:flex;"><span>         <span style="color:#007020;font-weight:bold">break</span>
</span></span><span style="display:flex;"><span>     <span style="color:#007020;font-weight:bold">default</span><span style="color:#666">:</span>
</span></span><span style="display:flex;"><span>         println <span style="color:#4070a0">&#34;Are you sure this is a valid day?&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Notice that Groovy&rsquo;s <code>switch</code> is a bit more powerful than C-like languages switches in that it is possible to use any kind of object in the switch and case. Instead of stacking up seven different case block with each enumerated value, you can regroup them in lists or ranges: whenever the value is in the list or the range, the case will be true and its associated instructions will be executed.</p>
<p>A more complex example inspired by the Java tutorial takes a more astronomical perspective on enums, and shows how your enums can have properties, constructors and methods:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">enum</span> <span style="color:#0e84b5;font-weight:bold">Planet</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>     MERCURY <span style="color:#666">(</span><span style="color:#40a070">3.303</span>e<span style="color:#666">+</span><span style="color:#40a070">23</span><span style="color:#666">,</span> <span style="color:#40a070">2.4397e6</span><span style="color:#666">),</span>
</span></span><span style="display:flex;"><span>     VENUS   <span style="color:#666">(</span><span style="color:#40a070">4.869</span>e<span style="color:#666">+</span><span style="color:#40a070">24</span><span style="color:#666">,</span> <span style="color:#40a070">6.0518e6</span><span style="color:#666">),</span>
</span></span><span style="display:flex;"><span>     EARTH   <span style="color:#666">(</span><span style="color:#40a070">5.976</span>e<span style="color:#666">+</span><span style="color:#40a070">24</span><span style="color:#666">,</span> <span style="color:#40a070">6.37814e6</span><span style="color:#666">),</span>
</span></span><span style="display:flex;"><span>     MARS    <span style="color:#666">(</span><span style="color:#40a070">6.421</span>e<span style="color:#666">+</span><span style="color:#40a070">23</span><span style="color:#666">,</span> <span style="color:#40a070">3.3972e6</span><span style="color:#666">),</span>
</span></span><span style="display:flex;"><span>     JUPITER <span style="color:#666">(</span><span style="color:#40a070">1.9</span>e<span style="color:#666">+</span><span style="color:#40a070">27</span><span style="color:#666">,</span>   <span style="color:#40a070">7.1492e7</span><span style="color:#666">),</span>
</span></span><span style="display:flex;"><span>     SATURN  <span style="color:#666">(</span><span style="color:#40a070">5.688</span>e<span style="color:#666">+</span><span style="color:#40a070">26</span><span style="color:#666">,</span> <span style="color:#40a070">6.0268e7</span><span style="color:#666">),</span>
</span></span><span style="display:flex;"><span>     URANUS  <span style="color:#666">(</span><span style="color:#40a070">8.686</span>e<span style="color:#666">+</span><span style="color:#40a070">25</span><span style="color:#666">,</span> <span style="color:#40a070">2.5559e7</span><span style="color:#666">),</span>
</span></span><span style="display:flex;"><span>     NEPTUNE <span style="color:#666">(</span><span style="color:#40a070">1.024</span>e<span style="color:#666">+</span><span style="color:#40a070">26</span><span style="color:#666">,</span> <span style="color:#40a070">2.4746e7</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>     <span style="color:#902000">double</span> mass
</span></span><span style="display:flex;"><span>     <span style="color:#902000">double</span> radius
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>     <span style="color:#06287e">Planet</span><span style="color:#666">(</span><span style="color:#902000">double</span> mass<span style="color:#666">,</span> <span style="color:#902000">double</span> radius<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>         <span style="color:#007020;font-weight:bold">this</span><span style="color:#666">.</span><span style="color:#4070a0">mass</span> <span style="color:#666">=</span> mass<span style="color:#666">;</span>
</span></span><span style="display:flex;"><span>         <span style="color:#007020;font-weight:bold">this</span><span style="color:#666">.</span><span style="color:#4070a0">radius</span> <span style="color:#666">=</span> radius<span style="color:#666">;</span>
</span></span><span style="display:flex;"><span>     <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>     <span style="color:#902000">void</span> <span style="color:#06287e">printMe</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>         println <span style="color:#4070a0">&#34;${name()} has a mass of ${mass} &#34;</span> <span style="color:#666">+</span>
</span></span><span style="display:flex;"><span>                 <span style="color:#4070a0">&#34;and a radius of ${radius}&#34;</span>
</span></span><span style="display:flex;"><span>     <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>Planet<span style="color:#666">.</span><span style="color:#4070a0">EARTH</span><span style="color:#666">.</span><span style="color:#4070a0">printMe</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>Like annotations, enums in Groovy require a JDK 5+ to run on, as Java 5 bytecode is generated.</p>
<h3 id="static-imports">Static imports</h3>
<p>In our previous examples of enums, we always had to prefix the enumerated value with its parent enum class, but thanks to static imports (which work even on a JDK 1.4 runtime) we can save some characters by dropping the Planet prefix:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">static</span> Planet<span style="color:#666">.*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>SATURN<span style="color:#666">.</span><span style="color:#4070a0">printMe</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>No more Planet prefix. But of course, static imports aren&rsquo;t only available for enums, but also work for other classes and static fields. What about doing some math?</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">static</span> java<span style="color:#666">.</span><span style="color:#4070a0">lang</span><span style="color:#666">.</span><span style="color:#4070a0">Math</span><span style="color:#666">.*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">sin</span><span style="color:#666">(</span>PI <span style="color:#4070a0">/ 6) + cos(PI /</span> <span style="color:#40a070">3</span><span style="color:#666">)</span> <span style="color:#666">==</span> <span style="color:#40a070">1</span>
</span></span></code></pre></div><p>Both the static methods of <code>java.lang.Math</code> and its static constants were statically imported to make the expression more concise. But if the abbreviations of sine and cosine are not readable for you, you can use aliasing in Groovy with the as keyword:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">static</span> java<span style="color:#666">.</span><span style="color:#4070a0">lang</span><span style="color:#666">.</span><span style="color:#4070a0">Math</span><span style="color:#666">.</span><span style="color:#4070a0">PI</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">static</span> java<span style="color:#666">.</span><span style="color:#4070a0">lang</span><span style="color:#666">.</span><span style="color:#4070a0">Math</span><span style="color:#666">.</span><span style="color:#4070a0">sin</span> <span style="color:#007020;font-weight:bold">as</span> sine
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">static</span> java<span style="color:#666">.</span><span style="color:#4070a0">lang</span><span style="color:#666">.</span><span style="color:#4070a0">Math</span><span style="color:#666">.</span><span style="color:#4070a0">cos</span> <span style="color:#007020;font-weight:bold">as</span> cosine
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#06287e">sine</span><span style="color:#666">(</span>PI <span style="color:#4070a0">/ 6) + cosine(PI /</span> <span style="color:#40a070">3</span><span style="color:#666">)</span> <span style="color:#666">==</span> <span style="color:#40a070">1</span>
</span></span></code></pre></div><p>Aliasing also works with normal imports too, not just static imports, and it can be pretty handy for adding some shortcut notation to very long class names as found in many frameworks, or for renaming methods or constants with non-obvious names, or not following your naming convention standards.</p>
<h3 id="generics">Generics</h3>
<p>A somewhat more controversial feature of Java 5 also finds its way in the latest release of Groovy 1.5 with Generics. Initially, it may feel odd to add even more typing information to a dynamic language, after all. Java developers usually believe that because of type erasure (for backward compatibility reasons with older versions of Java) no information is left in the class bytecode to represent the generic type. However, this is wrong, since through the reflection API, you are able to introspect a class to discover the types of its fields or of its methods arguments with the generics details.</p>
<p>So for instance, when you declare of field of type <code>List&lt;String&gt;</code>, somewhere in the bytecode, this information is kept in the form of some meta-information, although this field is really just of type <code>List</code>. This kind of reflexive information is used by enterprise frameworks like JPA or Hibernate to be able to relate a collection of elements from an entity to the entity representing the type of these elements.</p>
<p>To put this into practice, let us check if the generics information is kept on class fields:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Talk</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>     String title
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Speaker</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>     String name
</span></span><span style="display:flex;"><span>     List<span style="color:#666">&lt;</span>Talk<span style="color:#666">&gt;</span> talks <span style="color:#666">=</span> <span style="color:#666">[]</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> me <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Speaker<span style="color:#666">(</span>
</span></span><span style="display:flex;"><span>     <span style="color:#002070;font-weight:bold">name:</span> <span style="color:#4070a0">&#39;Guillaume Laforge&#39;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>     <span style="color:#002070;font-weight:bold">talks:</span> <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>         <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">Talk</span><span style="color:#666">(</span><span style="color:#002070;font-weight:bold">title:</span> <span style="color:#4070a0">&#39;Groovy&#39;</span><span style="color:#666">),</span>
</span></span><span style="display:flex;"><span>         <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">Talk</span><span style="color:#666">(</span><span style="color:#002070;font-weight:bold">title:</span> <span style="color:#4070a0">&#39;Grails&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>     <span style="color:#666">])</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> talksField <span style="color:#666">=</span>  me<span style="color:#666">.</span><span style="color:#4070a0">class</span><span style="color:#666">.</span><span style="color:#4070a0">getDeclaredField</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;talks&#39;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> talksField<span style="color:#666">.</span><span style="color:#4070a0">genericType</span><span style="color:#666">.</span><span style="color:#4070a0">toString</span><span style="color:#666">()</span> <span style="color:#666">==</span>
</span></span><span style="display:flex;"><span>    <span style="color:#4070a0">&#39;java.util.Listt&lt;Talk&gt;&#39;</span>
</span></span></code></pre></div><p>We defined two classes: a Speaker class giving Talks at conferences. In the Speaker class, the talks property is of type <code>List&lt;Talk&gt;</code>. Then, we create a Speaker instance with two nice shortcuts for initializing the name and talks properties, and for creating a list of Talk instances. Once this setup code is ready, we retrieve the field representing the talks, and we check that the generic type information is correct: yes, <code>talks</code> is a <code>List</code>, but a <code>List</code> of <code>Talks</code>.</p>
<h3 id="covariant-return-types">Covariant return types</h3>
<p>In Java 5, if you have a method in a subclass with the same name and parameter types as in a parent class, but with a return type derived from the parent method&rsquo;s return type, then we can override the parent method. In Groovy 1.0, covariant return types were not supported. But in Groovy 1.5, you can use them. Additionally, if you are trying to override a method with a return type not deriving from the parent class method&rsquo;s return type, a compilation error will be thrown. Covariant return types also work with parameterized types.</p>
<p>Beyond the support of Java 5 features which brought a few additions to the language, a few other syntax enhancements have been introduced in Groovy 1.5, and we are going to discover them in the following section.</p>
<h2 id="syntax-additions">Syntax additions</h2>
<h3 id="elvis-operator">Elvis operator</h3>
<p>Apart from the Java 5 features that brought annotations, generics and enums into Groovy, a new operator finds its way into the language: ?: the Elvis operator. When you see the operator in question, you will easily guessed why it was nicknamed that way - if not, think in terms of Smiley. This new operator is, in fact, a shortcut notation for the ternary operator. How many times are you using the ternary operator to change the value of a variable if its content is null to assign it some default value? The typical case in Java is as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>String name <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Guillaume&#34;</span><span style="color:#666">;</span>
</span></span><span style="display:flex;"><span>String displayName <span style="color:#666">=</span> name <span style="color:#666">!=</span> <span style="color:#007020;font-weight:bold">null</span> <span style="color:#666">?</span> name <span style="color:#666">:</span> <span style="color:#4070a0">&#34;Unknown&#34;</span><span style="color:#666">;</span>
</span></span></code></pre></div><p>In Groovy, since the language is able to &ldquo;coerce&rdquo; types to boolean values as needed (for instance where conditional expressions are required like in if or while constructs), in this statement, we can omit the comparison to null, because when a String is null, it is coerced to false, so in Groovy, the statement would become:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>String name <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Guillaume&#34;</span>
</span></span><span style="display:flex;"><span>String displayName <span style="color:#666">=</span> name <span style="color:#666">?</span> name <span style="color:#666">:</span> <span style="color:#4070a0">&#34;Unknown&#34;</span>
</span></span></code></pre></div><p>However, you will still notice the repetition of the name variable, which would violate the DRY principle (Don&rsquo;t Repeat Yourself). As this construct is pretty common, the Elvis operator was introduced to simplify such recurring cases, and the statements become:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>String name <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Guillaume&#34;</span>
</span></span><span style="display:flex;"><span>String displayName <span style="color:#666">=</span> name <span style="color:#666">?:</span> <span style="color:#4070a0">&#34;Unknown&#34;</span>
</span></span></code></pre></div><p>The second occurrence of the name variable is simply omitted and the ternary operator is no more ternary and is shortened to this more concise form.</p>
<p>It is also worth noticing that there are no side effects to this new construct, since the first element (here the name) is not evaluated twice as it would be the case with the ternary operator, so there&rsquo;s no need to introduce some intermediate temporary value to hold the result of the first evaluation of the first element of the ternary operator.</p>
<h3 id="classical-for-loop">Classical for loop</h3>
<p>Although Groovy is not strictly speaking a 100% superset of Java, the Groovy syntax comes closer to the Java syntax after each release, and more and more Java code is also valid Groovy. The net benefit of this is that when you begin with Groovy, you can copy and paste Java code in your Groovy classes, and this should just work as expected. Then, over time, as you learn the language, you start throwing away the semi-colons which are not mandatory in Groovy, using GStrings (interpolated strings), or closures, etc. Groovy offers a rather flat learning curve to Java developers.</p>
<p>However, there was one omission to this Java-syntax compatibility in the fact the classical <code>for</code> loop inherited from Java&rsquo;s C background wasn&rsquo;t allowed in Groovy. Initially, the Groovy developers thought it was not the nicest syntax of all and preferred using the <code>for</code> / <code>in</code> construct which was more readable. But as the Groovy users regularly asked for this old construct to be also part of Groovy, the team decided to bring it back to Groovy.</p>
<p>With Groovy 1.5, you can either chose the Groovy <code>for</code> / <code>in</code>, or prefer the classical for loop:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">for</span> <span style="color:#666">(</span>i <span style="color:#007020;font-weight:bold">in</span> <span style="color:#40a070">0</span><span style="color:#666">..</span><span style="color:#40a070">9</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>     println i
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#06287e">for</span> <span style="color:#666">(</span><span style="color:#902000">int</span> i <span style="color:#666">=</span> <span style="color:#40a070">0</span><span style="color:#666">;</span> i <span style="color:#666">&lt;</span> <span style="color:#40a070">10</span><span style="color:#666">;</span> i<span style="color:#666">++)</span>
</span></span><span style="display:flex;"><span>     println i
</span></span></code></pre></div><p>At the end of the day, it is probably more a matter of taste, and long time Groovy users usually prefer the most concise syntax with the <code>for</code> / <code>in</code> loop instead.</p>
<h3 id="named-parameters-without-parenthesis">Named parameters without parenthesis</h3>
<p>With its malleable and concise syntax, and its advanced dynamic capabilities, <strong>Groovy is an ideal choice for implementing internal Domain-Specific Languages</strong>. When you want to share a common metaphor between subject matter experts and developers, you can leverage Groovy to create a dedicated business language which models the key concept and business rules of your application. An important aspect of these DSLs is to make the code very readable, and also easier to write by non-technical persons. To achieve this goal even further, the grammar of the language was tweaked to allow us to use named parameters without the surrounding parenthesis.</p>
<p>First of all, in Groovy, named parameters look like this:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>fund<span style="color:#666">.</span><span style="color:#4070a0">compare</span><span style="color:#666">(</span><span style="color:#002070;font-weight:bold">to:</span> benchmarkFund<span style="color:#666">,</span> <span style="color:#007020;font-weight:bold">in</span><span style="color:#666">:</span> euros<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>compare<span style="color:#666">(</span><span style="color:#002070;font-weight:bold">fund:</span> someFund<span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">to:</span> benchmark<span style="color:#666">,</span> <span style="color:#007020;font-weight:bold">in</span><span style="color:#666">:</span> euros<span style="color:#666">)</span>
</span></span></code></pre></div><p>By adding new properties to numbers — which is possible in Groovy but beyond the scope of this article - we can also write code like this:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>monster<span style="color:#666">.</span><span style="color:#4070a0">move</span><span style="color:#666">(</span><span style="color:#002070;font-weight:bold">left:</span> <span style="color:#40a070">3</span><span style="color:#666">.</span><span style="color:#4070a0">meters</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">at:</span> <span style="color:#40a070">5</span><span style="color:#666">.</span><span style="color:#4070a0">mph</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>Now by omitting parenthesis, the code can become a little clearer as shown below:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>fund<span style="color:#666">.</span><span style="color:#4070a0">compare</span> <span style="color:#002070;font-weight:bold">to:</span> benchmarkFund<span style="color:#666">,</span> <span style="color:#007020;font-weight:bold">in</span><span style="color:#666">:</span> euros
</span></span><span style="display:flex;"><span>compare <span style="color:#002070;font-weight:bold">fund:</span> someFund<span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">to:</span> benchmark<span style="color:#666">,</span> <span style="color:#007020;font-weight:bold">in</span><span style="color:#666">:</span> euros
</span></span><span style="display:flex;"><span>monster<span style="color:#666">.</span><span style="color:#4070a0">move</span> <span style="color:#002070;font-weight:bold">left:</span> <span style="color:#40a070">3</span><span style="color:#666">.</span><span style="color:#4070a0">meters</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">at:</span> <span style="color:#40a070">5</span><span style="color:#666">.</span><span style="color:#4070a0">mph</span>
</span></span></code></pre></div><p>Obviously, this is not a big difference, but each statement becomes closer to real plain English sentences, and removes the usual boiler-plate technical code of the host language. This little enhancement of the grammar of the Groovy language gives more options to the designers of the business DSL.</p>
<h2 id="improved-tooling-support">Improved tooling support</h2>
<p>A common show-stopper when Groovy was a young language, was the lack of good tooling support: both the tool chain and the IDE support weren&rsquo;t up to the task. Fortunately, with the maturity and success of Groovy and the Grails web framework, this situation has changed.</p>
<h2 id="introduction-of-the-joint-compiler">Introduction of the &ldquo;joint&rdquo; compiler</h2>
<p>Groovy is well-known for its transparent and seamless integration with Java. But this is not just about being able to call Java methods from Groovy scripts, no, the integration between both languages goes well beyond that. For instance, it is totally possible to have a Groovy class extending a Java class which in turns implements a Groovy interface, or vice versa. This is something other alternative languages don&rsquo;t alway support, unfortunately. However, when mixing Groovy and Java classes together, so far, you had to be careful when compiling both type of classes by cleverly choosing the order of compilation, and when cyclic dependencies were spanning both languages, you may have hit a &ldquo;chicken and egg&rdquo; problem. Fortunately with Groovy 1.5, this is not the case anymore, and thanks to a contribution from JetBrains, the makers of the award winning <a href="http://www.jetbrains/idea/">Java IDE IntelliJ IDEA</a>, a &ldquo;joint&rdquo; compiler is available with which you can compile both Groovy and Java sources together in one go without having to think about dependencies between classes.</p>
<p>If you want to use the joint compiler from the command-line, you can call the groovyc command as usual, but specify the -j flag which will enable the joint compilation:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>groovyc *.groovy *.java -j -Jsource<span style="color:#666">=</span>1.4 -Jtarget<span style="color:#666">=</span>1.4
</span></span></code></pre></div><p>For passing parameters to the underlying javac command, you can prefix the flags with the J prefix. You can also use the joint compiler through its Ant task from you Ant or Maven build files:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;taskdef</span> <span style="color:#4070a0">name=</span><span style="color:#4070a0">&#34;groovyc&#34;</span>
</span></span><span style="display:flex;"><span>         <span style="color:#4070a0">classname=</span><span style="color:#4070a0">&#34;org.codehaus.groovy.ant.Groovyc&#34;</span>
</span></span><span style="display:flex;"><span>         <span style="color:#4070a0">classpathref=</span><span style="color:#4070a0">&#34;my.classpath&#34;</span><span style="color:#062873;font-weight:bold">/&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;groovyc</span>
</span></span><span style="display:flex;"><span>     <span style="color:#4070a0">srcdir=</span><span style="color:#4070a0">&#34;${mainSourceDirectory}&#34;</span>
</span></span><span style="display:flex;"><span>     <span style="color:#4070a0">destdir=</span><span style="color:#4070a0">&#34;${mainClassesDirectory}&#34;</span>
</span></span><span style="display:flex;"><span>     <span style="color:#4070a0">classpathref=</span><span style="color:#4070a0">&#34;my.classpath&#34;</span>
</span></span><span style="display:flex;"><span>     <span style="color:#4070a0">jointCompilationOptions=</span><span style="color:#4070a0">&#34;-j -Jsource=1.4 -Jtarget=1.4&#34;</span> <span style="color:#062873;font-weight:bold">/&gt;</span>
</span></span></code></pre></div><h3 id="maven-plugin-for-groovy">Maven plugin for Groovy</h3>
<p>For the Maven users, there is also a fully featured Maven plugin hosted at Codehaus which allows you to build your Java / Groovy applications: compile your Groovy and Java code, generate documentation from the JavaDoc tags, or it even lets you write your own Maven plugins in Groovy. There is also a Maven archetype to bootstrap your Groovy project more rapidly. For more information, you may have a look at the documentation of the plugin: <a href="http://mojo.codehaus.org/groovy/index.html">http://mojo.codehaus.org/groovy/index.html</a></p>
<h3 id="the-groovydoc-documentation-tool">The GroovyDoc documentation tool</h3>
<p>As a Java developer, you are used to documenting your code through JavaDoc tags in the comments of your classes, interfaces, fields, or methods. In Groovy, you can also use such tags in your comments, and have them being used by a tool called GroovyDoc to generate the equivalent JavaDoc documentation for all your Groovy classes.</p>
<p>There&rsquo;s an Ant task you can define then use to generate the documentation as follows:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;taskdef</span> <span style="color:#4070a0">name=</span><span style="color:#4070a0">&#34;groovydoc&#34;</span>
</span></span><span style="display:flex;"><span>     <span style="color:#4070a0">classname=</span><span style="color:#4070a0">&#34;org.codehaus.groovy.ant.Groovydoc&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>
</span></span><span style="display:flex;"><span>     <span style="color:#062873;font-weight:bold">&lt;classpath&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;path</span> <span style="color:#4070a0">path=</span><span style="color:#4070a0">&#34;${mainClassesDirectory}&#34;</span><span style="color:#062873;font-weight:bold">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;path</span> <span style="color:#4070a0">refid=</span><span style="color:#4070a0">&#34;compilePath&#34;</span><span style="color:#062873;font-weight:bold">/&gt;</span>
</span></span><span style="display:flex;"><span>     <span style="color:#062873;font-weight:bold">&lt;/classpath&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/taskdef&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;groovydoc</span>
</span></span><span style="display:flex;"><span>     <span style="color:#4070a0">destdir=</span><span style="color:#4070a0">&#34;${docsDirectory}/gapi&#34;</span>
</span></span><span style="display:flex;"><span>     <span style="color:#4070a0">sourcepath=</span><span style="color:#4070a0">&#34;${mainSourceDirectory}&#34;</span>
</span></span><span style="display:flex;"><span>     <span style="color:#4070a0">packagenames=</span><span style="color:#4070a0">&#34;**.*&#34;</span> <span style="color:#4070a0">use=</span><span style="color:#4070a0">&#34;true&#34;</span>
</span></span><span style="display:flex;"><span>     <span style="color:#4070a0">windowtitle=</span><span style="color:#4070a0">&#34;Groovydoc&#34;</span> <span style="color:#4070a0">private=</span><span style="color:#4070a0">&#34;false&#34;</span><span style="color:#062873;font-weight:bold">/&gt;</span>
</span></span></code></pre></div><h2 id="new-interactive-shell-and-the-swing-console">New interactive shell and the Swing console</h2>
<p>Groovy distributions always contained two different shells: a command-line shell as well as a Swing console. Groovysh, the command-line shell, has never been very friendly in terms of interactions with its user: whenever you wanted to execute a statement, you had to type &lsquo;go&rsquo; or &rsquo;execute&rsquo; after each one, so that it got executed. For quick prototyping or playing with some new API, typing &lsquo;go&rsquo; each time was very cumbersome. The situation has changed in Groovy 1.5, since a new interactive shell is born. No need to type &lsquo;go&rsquo; anymore.</p>
<p>This new shell features several enhancements, such as the use of the JLine library which provides ANSI coloring, tab completion for commands, line editing capabilities. You can work with different script buffers, remember already imported classes, load existing scripts, save the current script to a file, browse the history, etc. For detailed explanations of the supported features, please have a look at the <a href="http://groovy.codehaus.org/Groovy+Shel">documentation</a>.</p>
<p>The command-line shell isn&rsquo;t the only one that received some care, the Swing console has also been improved, with a new toolbar, with advanced undo capabilities, the possibility to increase or decrease the font size, syntax highlighting. A lot of polishing has been applied to the console.</p>
<h3 id="intellij-idea-jetgroovy-plugin">IntelliJ IDEA JetGroovy plugin</h3>
<p>I will save the best of tooling support till the end of this section by mentioning the JetGroovy plugin: a free and Open Source IntelliJ IDEA plugin dedicated to the support of both Groovy and Grails. This plugin has been developed by JetBrains themselves, and provides unmatched support for both the language and the web framework.</p>
<p>To list a few of the available features of the Groovy support:</p>
<ul>
<li><strong>Syntax highlighting</strong> for all the syntax, plus different warnings for types not recognized, or when static type information is not known to help spot potential errors.</li>
<li>Ability to <strong>run Groovy classes, scripts and JUnit test cases written in Groovy</strong>.</li>
<li><strong>Debugger</strong>: You can debug step by step across your Java and Groovy code, set breakpoints, show variables, the current stack, etc.</li>
<li>Joint compiler: the compiler <strong>compiles both Groovy and Java</strong> classes together and is able to resolve dependencies between both languages.</li>
<li><strong>Code completion</strong> for packages, classes, properties, fields, variables, methods, keywords, and even specific support for the Swing UI builder.</li>
<li>Advanced class search and find usage.</li>
<li><strong>Refactorings</strong>: most of the usual refactorings you&rsquo;ve come to love in Java are available as well and work across Java and Groovy, like &ldquo;surround with&rdquo;, introduce, inline or rename a variable, renaming for packages, classes, methods, and fields.</li>
<li><strong>Imports optimizations and code formatting</strong>.</li>
<li>Structure view: to have a bird&rsquo;s eye view of your classes.</li>
</ul>
<p>At the end of the day, you won&rsquo;t even notice whether you&rsquo;re developing a class in Groovy or in Java, considering the level of interplay and support offered inside IntelliJ IDEA. This is definitely a plugin to install if you&rsquo;re considering adding some dose of Groovy in your Java project, or if you plan to develop Grails applications.</p>
<p>More information can be found on <a href="http://www.jetbrains.net/confluence/display/GRVY/Groovy+Home">JetBrains website</a>.</p>
<p>Although I&rsquo;m only mentioning the plugin for IntelliJ IDEA, for your Groovy developments, you don&rsquo;t have to change your habits. You can also use the Eclipse plugin which is regularly improved by the IBM Project Zero developers, or Sun&rsquo;s brand new support of Groovy and Grails in NetBeans.</p>
<h2 id="performance-improvements">Performance improvements</h2>
<p>Along with new features, this new release of Groovy brings noticeable performance improvements as well as lower memory consumption, compared to previous versions. In our informal benchmarks measuring the duration of all our test suites, we noticed speed improvements between 15% to 45% compared with our beta versions of Groovy 1.5 - and certainly higher figures can be expected by comparing with the now old Groovy 1.0. More formal benchmarks have yet to be developed, but those figures have also been confirmed by developers from an insurance company that is using Groovy to write the business rules of their policy risk calculation engine, and from another company who ran several tests on highly parallel machines. Overall, Groovy 1.5 should be faster and leaner in most situations. Your mileage may vary depending on your own context of usage of Groovy.</p>
<h2 id="enhanced-dynamic-capabilities">Enhanced dynamic capabilities</h2>
<p>Through the symbiotic relationships between the Groovy and Grails projects, new dynamic capabilities have been introduced in Groovy, after having matured in the heart of Grails.</p>
<p>Groovy is a dynamic language: put simply, it means that certain things like method dispatch happens at runtime, instead of at compile-time as it is the case of Java and other languages. There is a specific runtime system, called the MOP (stands for Meta-Object Protocol) that is responsible for the dispatching logic. Fortunately, this runtime system is open enough so that people can hook into the system and change its usual behavior. For each Java class and for each Groovy instance, there is an associated meta-class which represents this runtime behavior of your objects. Groovy offered various ways to interact with the MOP by defining custom meta-classes by extending some base class, but thanks to the contribution from the Grails project, a groovier kind of meta-class is available: the expando meta-class.</p>
<p>Again,code samples are easier to help us understand the concept. In the following example, the <code>msg</code> String instance has got a meta-class that we can access through the metaClass property. Then we change the meta-class of the <code>String</code> class to add a new method called up to the String class, to have a shortcut notation for the <code>toUpperCase()</code> method. To do so, we assign a closure to the up property of the meta-class which is created as we assign it the closure. This closure takes no argument (hence why it starts with an arrow), and we call the <code>toUpperCase()</code> method on the delegate of the closure, which is a special closure variable which represents the real object (here the String instance).</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> msg <span style="color:#666">=</span> <span style="color:#4070a0">&#34;Hello!&#34;</span>
</span></span><span style="display:flex;"><span>println msg<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>String<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">up</span> <span style="color:#666">=</span> <span style="color:#666">{</span> <span style="color:#666">-&gt;</span> delegate<span style="color:#666">.</span><span style="color:#4070a0">toUpperCase</span><span style="color:#666">()</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">assert</span> <span style="color:#4070a0">&#34;HELLO!&#34;</span> <span style="color:#666">==</span> msg<span style="color:#666">.</span><span style="color:#4070a0">up</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>Through this meta-class, you can query the methods and/or properties which are available:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// print all the methods
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>obj<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">methods</span><span style="color:#666">.</span><span style="color:#4070a0">each</span> <span style="color:#666">{</span> println it<span style="color:#666">.</span><span style="color:#4070a0">name</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// print all the properties
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>obj<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">properties</span><span style="color:#666">.</span><span style="color:#4070a0">each</span> <span style="color:#666">{</span> println it<span style="color:#666">.</span><span style="color:#4070a0">name</span> <span style="color:#666">}</span>
</span></span></code></pre></div><p>You can even check that a certain method or property is available, with a finer granularity than through any instanceof check:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> msg <span style="color:#666">=</span> <span style="color:#4070a0">&#39;Hello!&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>msg<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">respondsTo</span><span style="color:#666">(</span>msg<span style="color:#666">,</span> <span style="color:#4070a0">&#39;toUpperCase&#39;</span><span style="color:#666">))</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    println msg<span style="color:#666">.</span><span style="color:#4070a0">toUpperCase</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>msg<span style="color:#666">.</span><span style="color:#4070a0">metaClass</span><span style="color:#666">.</span><span style="color:#4070a0">hasProperty</span><span style="color:#666">(</span>msg<span style="color:#666">,</span> <span style="color:#4070a0">&#39;bytes&#39;</span><span style="color:#666">))</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    println  foo<span style="color:#666">.</span><span style="color:#4070a0">bytes</span><span style="color:#666">.</span><span style="color:#4070a0">encodeBase64</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>These mechanisms are extensively used in the Grails web framework for instance to create dynamic finders: no need for DAOs in most circumstances, as you are able to call a <code>findByTitle()</code> dynamic method on a Book domain class. Through meta-classes, Grails automatically decorates the domain classes with such methods. Furthermore, if the method doesn&rsquo;t exist yet, it will be created and cached on first use. This can be accomplished by other advanced hooks as explained below.</p>
<p>Beyond those examples we&rsquo;ve seen so far, expando meta-classes also provide some complementary hooks. Four other kind of methods can be added to an expando meta-class:</p>
<ul>
<li><code>invokeMethod()</code> lets you intercept all methods calls,</li>
<li>while <code>methodMissing()</code> will be called on last resort only of no other method is found.</li>
<li><code>get</code>/<code>setProperty()</code> intercepts access to all properties,</li>
<li>whereas <code>propertyMissing()</code> is called when no property can be found.</li>
</ul>
<p>With expando meta-classes, customizing the behavior of the types of your application becomes easier and can save precious time of development compared with the previous version of Groovy. Obviously, not everybody needs to use those techniques, but they can be handy in a number of situations where you want to apply some AOP (Aspect Oriented Techniques) to decorate your classes, and when you want to simplify and make more readable the business code of your application, by removing some unnecessary boiler-plate code.</p>
<h2 id="swing-on-steroids">Swing on steroids</h2>
<p>The Groovy project has the chance to have a team of talented Swing developers who worked hard to enhance the capabilities of Groovy to build user interfaces in Swing. The basic brick for building Swing UIs in Groovy is the SwingBuilder class: at a syntactical level in your source, you can visually see how Swing components are nested within each other. A simplistic example from the Groovy website shows how to simply create a little GUI:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.swing.SwingBuilder</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">java.awt.BorderLayout</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.swing.SwingBuilder</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">java.awt.BorderLayout</span> <span style="color:#007020;font-weight:bold">as</span> BL
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> swing <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> SwingBuilder<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>count <span style="color:#666">=</span> <span style="color:#40a070">0</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> textlabel
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> frame <span style="color:#666">=</span> swing<span style="color:#666">.</span><span style="color:#4070a0">frame</span><span style="color:#666">(</span><span style="color:#002070;font-weight:bold">title:</span><span style="color:#4070a0">&#39;Frame&#39;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">size:</span><span style="color:#666">[</span><span style="color:#40a070">300</span><span style="color:#666">,</span><span style="color:#40a070">300</span><span style="color:#666">])</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    borderLayout<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    textlabel <span style="color:#666">=</span> label<span style="color:#666">(</span><span style="color:#002070;font-weight:bold">text:</span><span style="color:#4070a0">&#34;Clicked ${count} time(s).&#34;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>                      <span style="color:#002070;font-weight:bold">constraints:</span> BL<span style="color:#666">.</span><span style="color:#4070a0">NORTH</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    button<span style="color:#666">(</span><span style="color:#002070;font-weight:bold">text:</span><span style="color:#4070a0">&#39;Click Me&#39;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>              <span style="color:#002070;font-weight:bold">actionPerformed:</span> <span style="color:#666">{</span>count<span style="color:#666">++;</span> textlabel<span style="color:#666">.</span><span style="color:#4070a0">text</span> <span style="color:#666">=</span>
</span></span><span style="display:flex;"><span>              <span style="color:#4070a0">&#34;Clicked ${count} time(s).&#34;</span><span style="color:#666">;</span> println <span style="color:#4070a0">&#34;clicked&#34;</span><span style="color:#666">},</span>
</span></span><span style="display:flex;"><span>              <span style="color:#002070;font-weight:bold">constraints:</span>BorderLayout<span style="color:#666">.</span><span style="color:#4070a0">SOUTH</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>frame<span style="color:#666">.</span><span style="color:#4070a0">pack</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>frame<span style="color:#666">.</span><span style="color:#4070a0">show</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>In the novelties, the Swing builder concept has been extend to provide custom component factories. There are additional modules, not bundled with Groovy by default, which integrates the Swing components from JIDE or from the SwingX project into the usual Swing builder code.</p>
<p>Although this topic would deserve a full article, I&rsquo;m only going to list some of the other improvements in this release, for instance, the <code>bind()</code> method. Inspired by the beans binding JSR (JSR-295), you can easily bind components or beans together to have them react upon changes made on each other. In the following example, the size of the insets of the button will be changed according to the value of the slider component:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.swing.SwingBuilder</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">java.awt.Insets</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>swing <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> SwingBuilder<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>frame <span style="color:#666">=</span> swing<span style="color:#666">.</span><span style="color:#4070a0">frame</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>     vbox <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        slider<span style="color:#666">(</span><span style="color:#002070;font-weight:bold">id:</span> <span style="color:#4070a0">&#39;slider&#39;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">value:</span><span style="color:#40a070">5</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>        button<span style="color:#666">(</span><span style="color:#4070a0">&#39;Big Button?!&#39;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">margin:</span>
</span></span><span style="display:flex;"><span>             <span style="color:#666">**</span>bind<span style="color:#666">(</span><span style="color:#002070;font-weight:bold">source:</span> slider<span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>                  <span style="color:#002070;font-weight:bold">sourceProperty:</span><span style="color:#4070a0">&#39;value&#39;</span><span style="color:#666">,</span>
</span></span><span style="display:flex;"><span>                  <span style="color:#002070;font-weight:bold">converter:</span> <span style="color:#666">{</span> <span style="color:#666">[</span>it<span style="color:#666">,</span> it<span style="color:#666">,</span> it<span style="color:#666">,</span> it<span style="color:#666">]</span> <span style="color:#007020;font-weight:bold">as</span> Insets <span style="color:#666">}))</span>
</span></span><span style="display:flex;"><span>     <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>frame<span style="color:#666">.</span><span style="color:#4070a0">pack</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>frame<span style="color:#666">.</span><span style="color:#4070a0">size</span> <span style="color:#666">=</span> <span style="color:#666">[</span>frame<span style="color:#666">.</span><span style="color:#4070a0">width</span> <span style="color:#666">+</span> <span style="color:#40a070">200</span><span style="color:#666">,</span> frame<span style="color:#666">.</span><span style="color:#4070a0">height</span> <span style="color:#666">+</span> <span style="color:#40a070">200</span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>frame<span style="color:#666">.</span><span style="color:#4070a0">show</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>Binding components together is such a common task when building user interfaces, that this task has been simplified through this binding mechanism. There are also some other automatic binding options that can be used, but again, a dedicated article would probably be better.</p>
<p>In other new and noteworthy features, a few handy new methods have been added which leverage closures to call the infamous <code>SwingUtilities</code> class, and to start new threads: <code>edt()</code> will call <code>invokeAndWait()</code>, while <code>doLater()</code> will call <code>invokeLater()</code>, and <code>doOutside()</code> will just launch a closure in a new thread. No more ugly anonymous inner classes: just use closures through those shortcut methods!</p>
<p>Last but not least, separating the description of the view and its associated behavior logic has never been easier, thanks to the <code>build()</code> method on <code>SwingBuilder</code>. You can create a separate script which only contains the view, while the interactions or bindings between components are in the main class, making a clearer separation in the MVC model.</p>
<h2 id="summary">Summary</h2>
<p>In this article, the new and noteworthy features have been outlined, but we have barely scratched the surface of this new version of Groovy. The big highlights are mainly around the new Java 5 features, such as annotations, enums or generics: it makes Groovy perfectly capable of being integrated nicely and seamlessly with Enterprise frameworks such as Spring, Hibernate, or JPA. With the improvements in the syntax and with the enhanced dynamic capabilities, Groovy will let you customize your business logic by creating embedded Domain-Specific Languages, that you can easily integrate at the extension points of your application. The developer experience has progressed significantly through the work poured in the tooling support, this is no more a show stopper to the adoption of Groovy. Overall, with Groovy 1.5, the goal of simplifying the life of developers has never been so well fulfilled, and Groovy should definitely be part of all the Java developers&rsquo; toolbox.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>InfoQ: What's new in Groovy 1.5?</title><link>https://glaforge.dev/posts/2007/12/09/infoq-what-s-new-in-groovy-1-5/</link><pubDate>Sun, 09 Dec 2007 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2007/12/09/infoq-what-s-new-in-groovy-1-5/</guid><description>&lt;p>My &lt;a href="http://glaforge.free.fr/blog/groovy/224">announcement&lt;/a> of the release of Groovy 1.5 triggered the publication of an article that I wrote for InfoQ detailing the new features in this new release. If you want to know what it contains, I invite you to read my article on InfoQ: &lt;a href="http://www.infoq.com/articles/groovy-1.5-new">http://www.infoq.com/articles/groovy-1.5-new&lt;/a>&lt;/p>
&lt;p>And if you want to know a bit more about the &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> language history, or about my reasons to be part of this project, or if you want to know more about the founders behind &lt;a href="http://www.g2one.com/">G2One&lt;/a>, the Groovy and Grails company, here are a few pointers you may be interested in:&lt;/p></description><content:encoded>
<![CDATA[<p>My <a href="http://glaforge.free.fr/blog/groovy/224">announcement</a> of the release of Groovy 1.5 triggered the publication of an article that I wrote for InfoQ detailing the new features in this new release. If you want to know what it contains, I invite you to read my article on InfoQ: <a href="http://www.infoq.com/articles/groovy-1.5-new">http://www.infoq.com/articles/groovy-1.5-new</a></p>
<p>And if you want to know a bit more about the <a href="http://groovy.codehaus.org/">Groovy</a> language history, or about my reasons to be part of this project, or if you want to know more about the founders behind <a href="http://www.g2one.com/">G2One</a>, the Groovy and Grails company, here are a few pointers you may be interested in:</p>
<ul>
<li><a href="http://www.infoq.com/news/2007/12/interview-laforge-groovy">Interview of Guillaume Laforge</a> at QCon 2007, in March, in London</li>
<li><a href="http://www.javalobby.org/java/forums/t103434.html">G2One&rsquo;s team interview</a> on JavaLobby</li>
<li>A recent <a href="http://www.javalobby.org/java/forums/t103434.html">interview</a> of me by the excellent Geertjan Wielenga from Sun on JavaLobby, who asked very good questions.</li>
</ul>
<p>Let me give you a few other interesting pointers to presentations I&rsquo;ve given recently:</p>
<ul>
<li>My <a href="http://grails-exchange.com/guillaume-laforge#groovy-keynote">keynote </a>on Groovy at the <a href="http://www.grails-exchange.com/">Grails eXchange</a> conference in London (<a href="http://www.grails-exchange.com/files/Guillaume-Laforge-Groovy-Keynote-Grails-eXchange-2007.pdf">slides</a>)</li>
<li>A <a href="http://www.grails-exchange.com/files/Guilliaume%20LaForge%20-%20DomainSpecificLanguages.pdf">talk on Domain-Specific Languages in Groovy</a>, also given at the Grails eXchange conference</li>
<li>I&rsquo;ve also given a <a href="http://ijtc.firstport.ie/UserFiles/IJTC_2007_Guillaume-Laforge-Groovy.ppt">talk on Groovy</a> and one on <a href="http://ijtc.firstport.ie/lecture.aspx?lid=37">Grails</a> at the <a href="http://ijtc.firstport.ie/">Irish Java Technology Conference</a> in Dublin. This was the first conference of this new conference series, and for a start, that was really wonderful, well organized, with a nice lineup of speakers. Kudos to the organizers.</li>
</ul>
<p>And next week, I&rsquo;ll be at <a href="http://www.javapolis.com/confluence/display/JP07/Guillaume+Laforge">JavaPolis</a> with <a href="http://www.amazon.com/gp/product/1932394842/ref=cm_plog_item_link">Dierk Koenig</a> to present a general session on <a href="http://www.javapolis.com/confluence/display/JP07/Groovy+Update">what&rsquo;s new in Groovy 1.5</a>, and a BOF on the <a href="http://www.javapolis.com/confluence/display/JP07/Groovy+BOF+Update">cool things you can do with Groovy</a>. I&rsquo;ll be happy to meet you there if you&rsquo;re attending JavaPolis!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 1.5 released</title><link>https://glaforge.dev/posts/2007/12/07/groovy-1-5-released/</link><pubDate>Fri, 07 Dec 2007 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2007/12/07/groovy-1-5-released/</guid><description>&lt;p>&lt;a href="http://www.g2one.com/">G2One, Inc.&lt;/a>, the Groovy &amp;amp; Grails professional services company, and the Groovy development team are proud to announce the release of Groovy 1.5.&lt;/p>
&lt;p>Groovy is a &lt;strong>dynamic language for the JVM that integrates seamlessly with the Java platform&lt;/strong>. It offers a &lt;strong>Java-like syntax&lt;/strong>, with language features inspired by Smalltalk, Python or Ruby, and lets your reuse all your Java libraries and &lt;strong>protect the investment you made in Java skills, tools or application servers&lt;/strong>. Groovy can be used for various purposes, from adhoc shell scripting leveraging Java APIs, to full-blown web applications built on Spring and Hibernate through the &lt;a href="http://grails.org/">Grails&lt;/a> web framework. It can also be integrated very easily in your applications to externalize business logic, create &lt;strong>Domain-Specific Languages&lt;/strong>, or to provide templating, XML parsing capabilities, and much more.&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://www.g2one.com/">G2One, Inc.</a>, the Groovy &amp; Grails professional services company, and the Groovy development team are proud to announce the release of Groovy 1.5.</p>
<p>Groovy is a <strong>dynamic language for the JVM that integrates seamlessly with the Java platform</strong>. It offers a <strong>Java-like syntax</strong>, with language features inspired by Smalltalk, Python or Ruby, and lets your reuse all your Java libraries and <strong>protect the investment you made in Java skills, tools or application servers</strong>. Groovy can be used for various purposes, from adhoc shell scripting leveraging Java APIs, to full-blown web applications built on Spring and Hibernate through the <a href="http://grails.org/">Grails</a> web framework. It can also be integrated very easily in your applications to externalize business logic, create <strong>Domain-Specific Languages</strong>, or to provide templating, XML parsing capabilities, and much more.</p>
<p>This major release integrates features offered by Java 5: <strong>annotations, generics, static imports</strong> and <strong>enums</strong>, making Groovy the sole alternative dynamic language for the JVM that lets you leverage frameworks that use annotations like <strong>Spring</strong>&rsquo;s @Transactional or <strong>JBoss SEAM</strong> which both provide extended Groovy support, or generics to help <strong>JPA</strong> or <strong>Hibernate</strong> properly handle typed collections.</p>
<p>In this release, <strong>new meta-programming capabilities</strong> have been contributed thanks to the work of the Grails project developers, pursueing our symbiotic relationship. A few syntax enhancements have also found their way into it to help <strong>ease the development of Domain-Specific Languages</strong>. A great attention to <strong>performance improvements</strong> made this new version much snappier than before, as witnessed by a reports we had by teams working on mission-critical applications using Groovy as a business language.</p>
<p>Since Groovy 1.0, the team also worked on improving the tool chain by creating a <strong>joint Java / Groovy compiler</strong> to let you mix and match Groovy and Java classes in the same compilation step. A<strong>GroovyDoc</strong> equivalent to JavaDoc lets you document your Groovy classes. The <strong>interactive shell</strong> is now really interactive and provides useful command completions for making you more productive, and the<strong>Groovy Swing console</strong> has also been improved thanks to our talented Swing team and the help of Swing expert Romain Guy.</p>
<p>Apart from improvements or the creation of these new tools, you should have a look at JetBrains&rsquo; JetGroovy, a fantastic <strong>Groovy and Grails plugin</strong> which provides advanced coding capabilities to IntelliJ IDEA:</p>
<ul>
<li><strong>syntax highlighting</strong>,</li>
<li><strong>code completion</strong>,</li>
<li><strong>scripts and unit tests running ability</strong>,</li>
<li><strong>debugging capabilities</strong>,</li>
<li>and even <strong>refactorings</strong>!</li>
</ul>
<p>Of course, if you&rsquo;re an Eclipse user, you can still use the Groovy Eclipse plugin, or Sun&rsquo;s work in progress NetBeans plugin for Groovy and Grails.</p>
<p>An upcoming article on InfoQ that will be published in the following days will detail the novelties of this new version in more depth. So, please stay tuned!<br />
In the meantime, you can listen to the <a href="http://www.infoq.com/news/2007/12/interview-laforge-groovy">interview of Groovy Project Manager</a> and G2One VP Technology Guillaume Laforge that was recorded at QCon 2007, in March, in London or read <a href="http://www.javalobby.org/java/forums/t103434.html">G2One&rsquo;s team interview</a> on JavaLobby.</p>
<p>I would like to thank everybody who was involved in this release in a way or another: the Groovy developers for their hard work, patch and documentation contributors, users reporting bugs or requesting new features or improvements, book authors.</p>
<p>Several well-known companies have put great efforts in helping us making Groovy what it is today:</p>
<ul>
<li>
<p><strong>IBM</strong>: Eclipse plugin improvements and upgrade to support this latest version of the language,</p>
</li>
<li>
<p><strong>Oracle</strong>: JMX support improvements to call remote beans as if they were local,</p>
</li>
<li>
<p><strong>Sun</strong>: Rooms for our developer meetings, and a wonderful 8-core 8-CPU machine for our high-load concurrency testing,</p>
</li>
<li>
<p><strong>JetBrains</strong>: for the joint java/groovy compiler &amp; their awesome plugin,</p>
</li>
<li>
<p><strong>JBoss</strong>: for their help on ironing out our support for annotations and generics.</p>
</li>
</ul>
<p>It would be impossible to list everybody, but you&rsquo;re all part of this effort, and you made the success of Groovy, and the quality of this new milestone. Thanks and well done to you all!</p>
<p>You can read the detailed <a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;styleName=Html&amp;version=13904">JIRA release notes</a> of the changes since the last release candidate:</p>
<p>And now, just download Groovy 1.5 and give it a try:<br />
<a href="http://groovy.codehaus.org/Download">http://groovy.codehaus.org/Download</a></p>
<p>--<br />
Guillaume Laforge<br />
Groovy Project Manager<br />
Vice-President Technology at G2One, Inc.<br />
<a href="http://www.g2one.com/">http://www.g2one.com</a></p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>G2One: a Groovy and Grails company</title><link>https://glaforge.dev/posts/2007/10/10/g2one-a-groovy-and-grails-company/</link><pubDate>Wed, 10 Oct 2007 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2007/10/10/g2one-a-groovy-and-grails-company/</guid><description>&lt;p>As we&amp;rsquo;ve &lt;a href="http://www.nabble.com/Announcing-G2One%3A-The-Groovy-Grails-Company-tf4599726.html">announced &lt;/a>it on the Groovy and Grails mailing-lists today, &lt;a href="http://www.g2one.com/">G2One, Inc.&lt;/a> is born. Founded by &lt;a href="http://graemerocher.blogspot.com/2007/10/g2one-groovygrails-company-is-born.html">Graeme Rocher&lt;/a> (ex-CTO of &lt;a href="http://glaforge.free.fr/weblog/index.php?itemid=223">SkillsMatter&lt;/a> and Grails project lead), Alex Tkachman (ex-COO of &lt;a href="http://www.jetbrains.com/">JetBrains&lt;/a>makers of the &lt;a href="http://www.jetbrains.com/idea/">best Java IDE in the world&lt;/a>) and myself (Software Architect at &lt;a href="http://www.octo.com/">OCTO&lt;/a>, Groovy Project Manager and JSR-241 Spec Lead), the company will provide training, support, consulting, and commercial products around &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> and &lt;a href="http://grails.org/">Grails&lt;/a>, making &lt;strong>G2One the official source for Groovy and Grails expertise&lt;/strong>!&lt;/p>
&lt;p>It&amp;rsquo;s going to be a wonderful new adventure. After years spent working on Groovy at nights and week-ends, it&amp;rsquo;s going to be pretty refreshing, and it&amp;rsquo;s going to be a great opportunity to shift gears to spread the good word, iron out both projects, and at last working full-time on my pet projects.&lt;/p></description><content:encoded>
<![CDATA[<p>As we&rsquo;ve <a href="http://www.nabble.com/Announcing-G2One%3A-The-Groovy-Grails-Company-tf4599726.html">announced </a>it on the Groovy and Grails mailing-lists today, <a href="http://www.g2one.com/">G2One, Inc.</a> is born. Founded by <a href="http://graemerocher.blogspot.com/2007/10/g2one-groovygrails-company-is-born.html">Graeme Rocher</a> (ex-CTO of <a href="http://glaforge.free.fr/weblog/index.php?itemid=223">SkillsMatter</a> and Grails project lead), Alex Tkachman (ex-COO of <a href="http://www.jetbrains.com/">JetBrains</a>makers of the <a href="http://www.jetbrains.com/idea/">best Java IDE in the world</a>) and myself (Software Architect at <a href="http://www.octo.com/">OCTO</a>, Groovy Project Manager and JSR-241 Spec Lead), the company will provide training, support, consulting, and commercial products around <a href="http://groovy.codehaus.org/">Groovy</a> and <a href="http://grails.org/">Grails</a>, making <strong>G2One the official source for Groovy and Grails expertise</strong>!</p>
<p>It&rsquo;s going to be a wonderful new adventure. After years spent working on Groovy at nights and week-ends, it&rsquo;s going to be pretty refreshing, and it&rsquo;s going to be a great opportunity to shift gears to spread the good word, iron out both projects, and at last working full-time on my pet projects.</p>
<p><a href="http://www.grails-exchange.com/pcd/1001"><figure>
  <a href="#img-3f044ea8eff0cef0ff1b5431a1828340">
    <img src="http://www.grails-exchange.com/custom/images/Grails-exchange-im-speaking-@-280-84.jpg"
      alt="groovy, grails, java ee, grails exchange"
       title="Guillaume LaForge at Grails eXchange 2007" />
  </a>
  <figcaption>Guillaume LaForge at Grails eXchange 2007</figcaption>
</figure>
<div class="lightbox" id="img-3f044ea8eff0cef0ff1b5431a1828340">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="http://www.grails-exchange.com/custom/images/Grails-exchange-im-speaking-@-280-84.jpg"
    alt="groovy, grails, java ee, grails exchange"
     title="Guillaume LaForge at Grails eXchange 2007" />
  <div class="lightbox-caption">Guillaume LaForge at Grails eXchange 2007</div>
</div>
</a>Speaking of evangelism a little, I hope you (my faithful readers) won&rsquo;t miss the <a href="http://www.grails-exchange.com/">Grails eXchange</a> conference next week in London: it&rsquo;s going to be <strong>the</strong> place to be to learn <strong>everything you&rsquo;ve ever wanted to learn about Groovy and Grails</strong>. It&rsquo;s not too late to register, so hurry up before it&rsquo;s too late! It&rsquo;s a three-day conference featuring three tracks on Groovy, Grails and Java EE/AJAX/Web 2.0. There&rsquo;s a pretty nice line-up of <a href="http://www.grails-exchange.com/speakers/pcd/1001">speakers</a> from Google, Interface21, JBoss, Sun, etc, and the list of <a href="http://www.grails-exchange.com/programme-grails-exchange/pdc/1001">sessions</a> really looks awesome to me.</p>
<p>The coming months should be really amazing. First of all, at the end of the week, we&rsquo;ll release the first release candidate of Groovy 1.1. Then, next week, we&rsquo;ll be doing the 4th annual Groovy Developer Conference to pave the way for Groovy 2.0. Sun was very kind to host us this year again for this meeting (big thanks to Charlie for helping us). And mid-November, we&rsquo;ll release Groovy 1.1, a couple of weeks before the final release of Grails 1.0!</p>
<p>The pace of our Groovy world will really increase, my friends.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 1.1-beta-3 released, RC-1 and 1.1-final around the corner</title><link>https://glaforge.dev/posts/2007/09/20/groovy-1-1-beta-3-released-rc-1-and-1-1-final-around-the-corner/</link><pubDate>Thu, 20 Sep 2007 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2007/09/20/groovy-1-1-beta-3-released-rc-1-and-1-1-final-around-the-corner/</guid><description>&lt;p>Dear Groovy community,&lt;/p>
&lt;p>&lt;strong>Groovy 1.1-beta-3 is there&lt;/strong>, paving the way for an RC-1 in the following weeks, and if all goes well, for 1.1-final in October, right in time for the &lt;a href="http://www.grails-exchange.com/">Grails eXchange conference&lt;/a> that takes place in London. This conference will also be the opportunity for the Groovy developer team to meet for the &lt;strong>fourth Groovy Developer Conference&lt;/strong>! With Groovy 1.1 released by then, it&amp;rsquo;ll be time to think about what&amp;rsquo;s going to happen for the next major version of Groovy.&lt;/p></description><content:encoded>
<![CDATA[<p>Dear Groovy community,</p>
<p><strong>Groovy 1.1-beta-3 is there</strong>, paving the way for an RC-1 in the following weeks, and if all goes well, for 1.1-final in October, right in time for the <a href="http://www.grails-exchange.com/">Grails eXchange conference</a> that takes place in London. This conference will also be the opportunity for the Groovy developer team to meet for the <strong>fourth Groovy Developer Conference</strong>! With Groovy 1.1 released by then, it&rsquo;ll be time to think about what&rsquo;s going to happen for the next major version of Groovy.</p>
<p>Before going through the new release, let me recap some of the nice things that have been happening lately around Groovy:</p>
<ul>
<li><a href="http://www.jetbrains.com/">JetBrains</a> released a second milestone to the wonderful <a href="http://www.jetbrains.net/confluence/display/GRVY/Groovy+Home">Groovy &amp; Grails IntelliJ IDEA plugin</a>, so be sure to check it out, as you&rsquo;ll feel at ease developing Groovy with all the bells and whistles of your beloved IDE. You&rsquo;ve never programmed Groovy and Grails with so much pleasure.</li>
<li>IBM&rsquo;s <a href="http://www.projectzero.org/wiki/bin/view/">ProjectZero</a> team is also helping us improving the <a href="http://groovy.codehaus.org/Eclipse+Plugin">Eclipse plugin</a>.</li>
<li><a href="http://www.sun.com/">Sun</a> gave us access to a nice server beast so we can conduct some high-concurrency load testing on Groovy.</li>
</ul>
<p>So what&rsquo;s in this release you may wonder? Well, there are a few nice novelties, but they should be the last ones before 1.1.</p>
<p>First of all, Alex Tkachman, along with Jochen, worked very hard on <strong>improving the performance</strong> of Groovy. On some micro-benchmark seen on the blogosphere, we even got a 100% improvement. Of course, depending on your usage of Groovy, your mileage may vary, but let me congratulate Alex for this great achievement.</p>
<p>Now regarding new features:</p>
<ul>
<li>The last mile of Java 5 related feature is included: you can <strong>use and define enums in Groovy</strong>.</li>
<li>The closure and map coercion to interfaces mechanism has been extended to work on concrete classes too.</li>
<li>The ternary operator can be shortcut to simplify <code>a != null ? a : &quot;default value&quot;</code> into <code>a ?: &quot;default value&quot;</code>. We call it the <strong>Elvis operator</strong> &ndash; a beer for those who guess why we&rsquo;ve chosen that name.</li>
<li>In the dynamic space, Graeme Rocher has been continuing enhancing and <strong>improving the ExpandoMetaClass</strong> and has added some new methods like methodMissing(), respondsTo() or hasProperty(). Don&rsquo;t forget to check the <a href="http://groovy.codehaus.org/Dynamic+Groovy">documentation</a> and the child pages.</li>
<li>It is now possible to customize the <a href="http://fisheye.codehaus.org/browse/~author=graeme/groovy/trunk/groovy/groovy-core/src/test/groovy/lang/ClosureResolvingTest.groovy?r=6945">variable resolving strategy in closures</a> (not yet documented), so that you can decide whether you want to the resolution to go to the delegate first or only, to the closure itself, or to the owner.</li>
<li>Jason Dillon has been working on <strong>improving the good old Groovy Shell</strong> (groovysh). It is still a work-in-progress, so by default, it is not activated, but you may try it by setting a NEWSHELL environment variable to a dummy value. Some completion is there thanks to JLine, <strong>ANSI color</strong> makes things more friendly on most platforms (Windows being the exception as always), and the driving idea behind those evolutions was <strong>getting rid of the infamous &ldquo;go&rdquo; command</strong>.</li>
<li><a href="http://www.curious-creature.org/">Romain Guy</a>, on his side, along with the help of <a href="http://shemnon.com/speling/">Danno Ferrin</a> and <a href="http://www.jroller.com/aalmiray/">Andres Almiray</a>. have <strong>polished the Groovy Swing Console look&rsquo;n feel</strong>.</li>
<li>One last nugget, some improvements have been worked on to allow a better integration between the groovyc and javac Ant task letting you use the javac Ant task as a sub-element of the groovyc Ant task &ndash; however, for big projects with a lot of classes, it may be pretty hungry for memory.</li>
</ul>
<p>With all that, it&rsquo;s time to give the usual links. Apart from those new features or improvements, we&rsquo;ve closed a fair amount of bugs too, if you want to have a closer look at what we&rsquo;ve worked on, you can have a look at the <a href="http://jira.codehaus.org/secure/IssueNavigator.jspa?reset=true&amp;pid=10242&amp;fixfor=13590">JIRA issues closed for beta-3</a>.</p>
<p>You can <a href="http://groovy.codehaus.org/Download">download Groovy</a> at the usual place: Joachim Bauman is updating the <a href="http://groovy.codehaus.org/Windows+NSIS-Installer">Windows native installer</a> (which also contains Antti Karanta&rsquo;s <a href="http://glaforge.free.fr/weblog/index.php?itemid=222">native launcher</a> for Windows) and he should make it available in the following days.</p>
<p>One last closing word: the documentation of the website is available too, and over those past months, the <strong>documentation climbed to about 900 pages</strong> worth of PDF export! Even bigger than the fine <a href="http://www.manning.com/koenig/">GinA</a>lady!</p>
<p>Keep Groovying, thanks to all the developers and contributors for their help, and stay tuned for RC-1 and 1.1 pretty soon!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 1.1-beta-2 with contributions from JetBrains and JBoss</title><link>https://glaforge.dev/posts/2007/07/05/groovy-1-1-beta-2-with-contributions-from-jetbrains-and-jboss/</link><pubDate>Thu, 05 Jul 2007 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2007/07/05/groovy-1-1-beta-2-with-contributions-from-jetbrains-and-jboss/</guid><description>&lt;p>The Groovy team is pleased to announce the &lt;strong>release of Groovy 1.1-beta-2&lt;/strong>, yet another step on our aggressive roadmap towards the release of Groovy 1.1 in October.&lt;/p>
&lt;p>For this release, I would like especially to highlight two key contributions to the project:&lt;/p>
&lt;ul>
&lt;li>First of all, after we&amp;rsquo;ve added &lt;a href="http://docs.codehaus.org/display/GROOVY/2007/04/30/Groovy+1.1-beta-1+with+annotation+support">Java 5 annotation support in Groovy 1.1-beta-1&lt;/a>, this time, it was &lt;strong>generics&lt;/strong>&amp;rsquo; turn. Thanks to the help of some JBoss developers who&amp;rsquo;ve &lt;a href="http://blog.hibernate.org/cgi-bin/blosxom.cgi/Emmanuel%20Bernard/groovyseamed.html">integrated Groovy in JBoss Seam&lt;/a>, we&amp;rsquo;ve been able to test our &lt;strong>support for annotations and generics&lt;/strong>, and to make sure we would release a quality milestone to our users. &lt;strong>Groovy is the first alternative dynamic language for the JVM that supports annotations and generics&lt;/strong>, so that you can integrate Groovy with any Enterprise application frameworks like EJB 3 / JPA, &lt;a href="http://www.infoq.com/news/2007/06/seam2beta">JBoss Seam&lt;/a>, Google &lt;a href="http://glaforge.free.fr/weblog/index.php?itemid=208">Guice&lt;/a>, Spring, etc.&lt;/li>
&lt;li>Secondly, I&amp;rsquo;m very happy to report the &lt;strong>contribution of JetBrains to the development of Groovy&lt;/strong>. While working on the &lt;a href="http://groovy.codehaus.org/IntelliJ+IDEA+Plugin+(JetBrains+Edition)">IntelliJ IDEA plugin for Groovy and Grails&lt;/a>, the &lt;strong>talentuous JetBrains team provided us with a joint Java/Groovy compiler&lt;/strong>! No more nightmare to cleanly separate Java and Groovy code to avoid cyclic references and tedious build configuration, you can now use the Groovyc compiler to &lt;a href="http://blackdragsview.blogspot.com/2007/07/joint-compilation-in-groovy.html">compile both Groovy and Java&lt;/a> sources in a single step.&lt;/li>
&lt;/ul>
&lt;p>Apart from those two great contributions that we have integrated in the project, we&amp;rsquo;ve worked on many other areas since the release of the first beta:&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy team is pleased to announce the <strong>release of Groovy 1.1-beta-2</strong>, yet another step on our aggressive roadmap towards the release of Groovy 1.1 in October.</p>
<p>For this release, I would like especially to highlight two key contributions to the project:</p>
<ul>
<li>First of all, after we&rsquo;ve added <a href="http://docs.codehaus.org/display/GROOVY/2007/04/30/Groovy+1.1-beta-1+with+annotation+support">Java 5 annotation support in Groovy 1.1-beta-1</a>, this time, it was <strong>generics</strong>&rsquo; turn. Thanks to the help of some JBoss developers who&rsquo;ve <a href="http://blog.hibernate.org/cgi-bin/blosxom.cgi/Emmanuel%20Bernard/groovyseamed.html">integrated Groovy in JBoss Seam</a>, we&rsquo;ve been able to test our <strong>support for annotations and generics</strong>, and to make sure we would release a quality milestone to our users. <strong>Groovy is the first alternative dynamic language for the JVM that supports annotations and generics</strong>, so that you can integrate Groovy with any Enterprise application frameworks like EJB 3 / JPA, <a href="http://www.infoq.com/news/2007/06/seam2beta">JBoss Seam</a>, Google <a href="http://glaforge.free.fr/weblog/index.php?itemid=208">Guice</a>, Spring, etc.</li>
<li>Secondly, I&rsquo;m very happy to report the <strong>contribution of JetBrains to the development of Groovy</strong>. While working on the <a href="http://groovy.codehaus.org/IntelliJ+IDEA+Plugin+(JetBrains+Edition)">IntelliJ IDEA plugin for Groovy and Grails</a>, the <strong>talentuous JetBrains team provided us with a joint Java/Groovy compiler</strong>! No more nightmare to cleanly separate Java and Groovy code to avoid cyclic references and tedious build configuration, you can now use the Groovyc compiler to <a href="http://blackdragsview.blogspot.com/2007/07/joint-compilation-in-groovy.html">compile both Groovy and Java</a> sources in a single step.</li>
</ul>
<p>Apart from those two great contributions that we have integrated in the project, we&rsquo;ve worked on many other areas since the release of the first beta:</p>
<ul>
<li>We have ironed out the usage of Java 5 annotations (for instance <a href="http://docs.codehaus.org/display/GROOVY/2007/06/14/The+annotation+support+has+been+extended+to+include+support+for+Method+Parameter+Annotations">annotations for method parameters</a> were missing).</li>
<li>As I&rsquo;ve already mentioned, we&rsquo;ve added <strong>support for generics</strong> in Groovy, so that the generated bytecode properly includes the reflection information needed at runtime by various tools such as JPA. In the area of Java 5 features, <a href="http://groovy.codehaus.org/Static+Import+Usage">static imports</a> were also available in Groovy in beta-1 to make the code even more concise and readable.</li>
<li>Still pursuing the <strong>symbiotic relationship between the Groovy and Grails projects</strong>, the Grails team has integrated in Groovy its new <a href="http://docs.codehaus.org/display/GROOVY/ConfigSlurper">ConfigSlurper</a> mechanism to configure your applications more easily.</li>
<li>The <strong>classical for loop</strong> is now back in Groovy after a lot of user requests.</li>
<li>Named parameters are also now possible on top-level statements, without parentheses, so that expressive code can be written like in: move x: 10.centimers, y: 8;centimeters or fund.compareWith bench: NIKEI, over: 2.months</li>
</ul>
<p>Apart from that, we also focused on cleaning up the Groovy grammar, improving the code coverage and the performance of Groovy in highly concurrent scenarios for paralle machines. Overall, roughly a hundred bug fixes, enhancements or new features have been integrated in this new release. On the tooling front, the <a href="http://docs.codehaus.org/display/GROOVY/2007/06/29/Version+1.0.1+of++the+Groovy+Eclipse+Plugin+is+now+available.">Eclipse plugin recently released version 1.0.1</a>, while the progress on the <a href="http://www.jetbrains.net/confluence/display/GRVY/Groovy+Home">IntelliJ IDEA plugin</a> has been awesome, you should also check it out.</p>
<p>You can download Groovy 1.1-beta-2 from the <a href="http://groovy.codehaus.org/Download">download area</a>. You can find the full release notes in our <a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;styleName=Html&amp;version=10436">JIRA bug tracker</a>.</p>
<p>Thanks to all users, contributors and committers for allowing us to make this great release.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>QOTD: Eclipse is the PC of IDEs when IntelliJ IDEA is the Mac</title><link>https://glaforge.dev/posts/2007/06/22/qotd-eclipse-is-the-pc-of-ides-when-intellij-idea-is-the-mac/</link><pubDate>Fri, 22 Jun 2007 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2007/06/22/qotd-eclipse-is-the-pc-of-ides-when-intellij-idea-is-the-mac/</guid><description>&lt;p>Funny &lt;a href="http://www.nabble.com/Status-of-the-eclipse-plug-in--tf3962848.html#a11256927">quote on the Groovy user mailing-list&lt;/a>, when someone was complaining that &lt;a href="http://www.jetbrains.com/idea/">IntelliJ IDEA&lt;/a> wasn&amp;rsquo;t Open-Source:&lt;/p>
&lt;blockquote>
&lt;p>I want a platform that &amp;ldquo;just works&amp;rdquo; i don&amp;rsquo;t care what it looks like underneath. It is why I have a Mac with Mac OS X and why I use &lt;a href="http://www.jetbrains.com/idea/">IntelliJ IDEA&lt;/a>. Eclipse is the PC of IDEs. When you&amp;rsquo;re younger and have the time and energy to spend hours settings things up, dealing with driver problems (read plugins), install things over and over and deal with the incomptabilities between different drivers (read plugins) you&amp;rsquo;re ok with a PC (read Eclipse)&lt;/p></description><content:encoded>
<![CDATA[<p>Funny <a href="http://www.nabble.com/Status-of-the-eclipse-plug-in--tf3962848.html#a11256927">quote on the Groovy user mailing-list</a>, when someone was complaining that <a href="http://www.jetbrains.com/idea/">IntelliJ IDEA</a> wasn&rsquo;t Open-Source:</p>
<blockquote>
<p>I want a platform that &ldquo;just works&rdquo; i don&rsquo;t care what it looks like underneath. It is why I have a Mac with Mac OS X and why I use <a href="http://www.jetbrains.com/idea/">IntelliJ IDEA</a>. Eclipse is the PC of IDEs. When you&rsquo;re younger and have the time and energy to spend hours settings things up, dealing with driver problems (read plugins), install things over and over and deal with the incomptabilities between different drivers (read plugins) you&rsquo;re ok with a PC (read Eclipse)</p>
<p>When you get passed this phase and just want to get things done on a platform that doesn&rsquo;t get in the way and becomes invasive then you choose a platform that does this for you like the Mac (read IntelliJ). It just works out the box and everythig is nicely integrated, including the <a href="http://groovy.codehaus.org/IntelliJ+IDEA+Plugin+(JetBrains+Edition)">Groovy plugin</a> ;-)</p></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>G2One, the Groovy and Grails meetup at JavaOne</title><link>https://glaforge.dev/posts/2007/05/01/g2one-the-groovy-and-grails-meetup-at-javaone/</link><pubDate>Tue, 01 May 2007 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2007/05/01/g2one-the-groovy-and-grails-meetup-at-javaone/</guid><description>&lt;p>If you are attending JavaOne and are already in San Francisco on Monday, you have to come to &lt;a href="http://www.nofluffjuststuff.com/groovygrailsmeetup/registration/register">G2One, the Groovy and Grails meetup&lt;/a>. The fine folks of the &lt;a href="http://www.nofluffjuststuff.com/">NFJS&lt;/a> tour are hosting this meeting to gather the Groovy and Grails community. G2One is taking place at the &lt;a href="http://www.google.com/maps?q=181+3rd+St,+San+Francisco,+California+94103,+USA&amp;amp;ie=UTF8&amp;amp;z=16&amp;amp;om=1&amp;amp;iwloc=addr">W Hotel&lt;/a> near the Moscone Center, on Monday evening. It&amp;rsquo;s the best opportunity to meet the people who make Groovy and Grails.&lt;/p>
&lt;p>Will be present for presentations, demos, and panel discussions:&lt;/p></description><content:encoded>
<![CDATA[<p>If you are attending JavaOne and are already in San Francisco on Monday, you have to come to <a href="http://www.nofluffjuststuff.com/groovygrailsmeetup/registration/register">G2One, the Groovy and Grails meetup</a>. The fine folks of the <a href="http://www.nofluffjuststuff.com/">NFJS</a> tour are hosting this meeting to gather the Groovy and Grails community. G2One is taking place at the <a href="http://www.google.com/maps?q=181+3rd+St,+San+Francisco,+California+94103,+USA&amp;ie=UTF8&amp;z=16&amp;om=1&amp;iwloc=addr">W Hotel</a> near the Moscone Center, on Monday evening. It&rsquo;s the best opportunity to meet the people who make Groovy and Grails.</p>
<p>Will be present for presentations, demos, and panel discussions:</p>
<ul>
<li><a href="http://glaforge.free.fr/blog/groovy">Guillaume Laforge</a> (<a href="http://groovy.codehaus.org/">Groovy</a> Project Manager),</li>
<li><a href="http://graemerocher.blogspot.com/">Graeme Rocher</a> (<a href="http://grails.org/">Grails</a> Project Lead and author of <a href="http://www.apress.com/book/bookDisplay.html?bID=10205">The Definitive Guide to Grails</a>),</li>
<li><a href="http://www.amazon.com/gp/blog/A368TUB0Q1IE3F">Dierk Koenig</a> (author of <a href="http://www.manning.com/koenig/">Groovy in Action</a>),</li>
<li><a href="http://www.thediscoblog.com/">Andrew Glover</a> (co-author of <a href="http://www.manning.com/koenig/">Groovy in Action</a>),</li>
<li><a href="http://jasonrudolph.com/">Jason Rudolph</a> (author of <a href="http://www.infoq.com/minibooks/grails">Getting Started with Grails</a>),</li>
<li><a href="http://memeagora.blogspot.com/">Neal Ford</a> (Dynamic Language expert).</li>
</ul>
<p>You can consult the <a href="http://www.nofluffjuststuff.com/groovygrailsmeetup/registration/register">agenda </a>and <a href="http://www.nofluffjuststuff.com/groovygrailsmeetup/registration/register">register online</a>. Hurry up, there&rsquo;s only 250 places left! You can even win copies of the Groovy and Grails books, and a Playstation 3! We&rsquo;ll all be delighted to keep you all up-to-date with what&rsquo;s going on in the Groovy and Grails community.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 1.1-beta-1 with annotation support</title><link>https://glaforge.dev/posts/2007/04/30/groovy-1-1-beta-1-with-annotation-support/</link><pubDate>Mon, 30 Apr 2007 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2007/04/30/groovy-1-1-beta-1-with-annotation-support/</guid><description>&lt;p>After &lt;a href="http://glaforge.free.fr/weblog/index.php?itemid=210">Groovy was awarded the first prize of the JAX conference&lt;/a> in Germany last week for being the &lt;strong>most innovative and creative project in 2007 in the Java community&lt;/strong>, we&amp;rsquo;re pleased to announce the&lt;strong>release of Groovy 1.1-beta-1&lt;/strong>.&lt;/p>
&lt;p>This release is the first beta release after the &lt;a href="http://glaforge.free.fr/weblog/index.php?itemid=200">release of Groovy 1.0&lt;/a>. But it&amp;rsquo;s a very important release as we&amp;rsquo;ve been working on key features putting Groovy clearly as the &lt;strong>de facto enterprise scripting solution&lt;/strong>. Indeed, Groovy is now the first and sole alternative language for the JVM that &lt;strong>supports Java 5 annotations&lt;/strong>. Groovy 1.1-beta-1 also supports Java 5 &lt;strong>static imports&lt;/strong>.&lt;/p></description><content:encoded>
<![CDATA[<p>After <a href="http://glaforge.free.fr/weblog/index.php?itemid=210">Groovy was awarded the first prize of the JAX conference</a> in Germany last week for being the <strong>most innovative and creative project in 2007 in the Java community</strong>, we&rsquo;re pleased to announce the<strong>release of Groovy 1.1-beta-1</strong>.</p>
<p>This release is the first beta release after the <a href="http://glaforge.free.fr/weblog/index.php?itemid=200">release of Groovy 1.0</a>. But it&rsquo;s a very important release as we&rsquo;ve been working on key features putting Groovy clearly as the <strong>de facto enterprise scripting solution</strong>. Indeed, Groovy is now the first and sole alternative language for the JVM that <strong>supports Java 5 annotations</strong>. Groovy 1.1-beta-1 also supports Java 5 <strong>static imports</strong>.</p>
<p>You can now use Groovy to write your <a href="http://www.curious-creature.org/2007/03/25/persistence-made-easy-with-groovy-and-jpa/">EJB 3 / JPA beans</a>, to wire your components with <a href="http://glaforge.free.fr/weblog/index.php?itemid=208">Google Guice</a>, to <a href="http://jroller.com/page/buggybean?entry=using_groovy_spring_and_javaconfig1">mark your services transactional</a> with Spring&rsquo;s <code>@Transactional</code> annotation, to develop <a href="http://www.jboss.com/products/seam">JBoss Seam</a>entities, or to be able to write unit tests with <a href="http://groovy.codehaus.org/Using+JUnit+4+with+Groovy">JUnit 4</a> or <a href="http://code.google.com/p/testngroove/">TestNG</a>. If you want the best and <strong>seamless Java integration</strong>, and be able to use the latest frameworks around that leverage Java 5 annotations, look no further, Groovy is the unique solution to your scripting and dynamic needs. Whether you hack a script in your shell, or if you want to implement <a href="http://groovy.codehaus.org/Writing+Domain-Specific+Languages">Domain-Specific Languages</a>, Groovy has everything you need to make you more productive.</p>
<p>Of course, this release contains a nice list of bug fixes and improvements, making Groovy a very stable and viable platform on its own, or for your integration needs. You can find the list of all the <a href="http://jira.codehaus.org/browse/GROOVY">bugs and improvements on JIRA</a>.</p>
<p>Just to highlight some of the other interesting gems you&rsquo;ll find in this release, note that <a href="http://groovy.codehaus.org/Groovy+Mocks">Groovy mocks</a> now let&rsquo;s you deal with properties as well, you can group list and map elements with a discriminator closure with the groupBy() method from the GDK, the use directive now returns the value returned by the closure it is passed, and you can add maps together. All in all, minor features, but which might come in handy from time to time. A more interesting one is probably the <a href="http://groovy.codehaus.org/Groovy+Mocks">ExpandoMetaClass</a> from Grails has been brought back to Groovy.</p>
<p>You can download Groovy from the <a href="http://groovy.codehaus.org/Download">usual location</a>. You&rsquo;ll be able to download the <a href="http://dist.groovy.codehaus.org/distributions/groovy-binary-1.1-BETA-1.zip">binary distribution</a>, the <a href="http://dist.groovy.codehaus.org/distributions/groovy-src-1.1-BETA-1.zip">source distribution</a>, and a <a href="http://dist.groovy.codehaus.org/distributions/groovy-docs-1.1-BETA-1.zip">zip of the online documentation</a> (over 500 pages) and the JavaDoc.</p>
<p>While the core Groovy team was working on the language, the <a href="http://groovy.codehaus.org/Eclipse+Plugin">Groovy Eclipse Plugin</a> team has been making awesome progress, and the (soon-to-be-released) plugin now sports <a href="http://www.rippleinteractive.com/blog/2007/04/10/1176254940000.html">good code-completion capabilities</a>. But we also have good news for the <a href="http://www.jetbrains/idea">IntelliJ IDEA</a> lovers, JetBrains is working full-steam to provide their own Groovy plugin, so stay tuned for additional news in that area.</p>
<p>A part of the Groovy and Grails team will be present at <a href="http://sun.com/javaone">JavaOne 2007</a> in San Francisco to present the cool things you can do with Groovy, or how Grails reivents Spring / Hibernate development, so please come and say hi if you&rsquo;re around. This year, Groovy and Grails will be very well represented, there will be <a href="http://www28.cplan.com/cc158/sessions_catalog.jsp?ilc=158-1&amp;ilg=english&amp;isort=&amp;isort_type=&amp;is=yes&amp;icriteria1=+&amp;icriteria2=+&amp;icriteria7=+&amp;icriteria9=&amp;icriteria8=groovy">8 Groovy sessions</a> and <a href="http://www28.cplan.com/cc158/sessions_catalog.jsp?ilc=158-1&amp;ilg=english&amp;isort=&amp;isort_type=&amp;is=yes&amp;icriteria1=+&amp;icriteria2=+&amp;icriteria7=+&amp;icriteria9=&amp;icriteria8=grails">3 Grails sessions</a>. Overall, twice as much as last year.</p>
<p>Additionaly, on Monday evening, just before JavaOne, there will be a special GroovyOne community event from 7pm to 10pm at the W Hotel organized by the fine folks from <a href="http://www.nofluffjuststuff.com/">NoFluffJustStuff</a> and the<a href="http://aboutgroovy.com/">AboutGroovy</a> community site. It will be a great opportunity to meet core Groovy and Grails developers, leads and book authors. More details to follow.</p>
<p>On behalf of the Groovy team, we wish you will enjoy this new release!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy.Net, annotations, mocks, applet, and so on</title><link>https://glaforge.dev/posts/2007/04/29/groovy-net-annotations-mocks-applet-and-so-on/</link><pubDate>Sun, 29 Apr 2007 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2007/04/29/groovy-net-annotations-mocks-applet-and-so-on/</guid><description>&lt;p>After &lt;a href="http://glaforge.free.fr/weblog/index.php?itemid=210">Groovy won the JAX 2007 innovation award&lt;/a>, I took some time to look at what was going on in the blogosphere. There&amp;rsquo;s always a lot of activity in the Groovy-sphere. It never ceases to amaze me how prolific the community is. Let&amp;rsquo;s list some of the interesting posts I&amp;rsquo;ve come across this week-end.&lt;/p>
&lt;ul>
&lt;li>Chanwit managed to make &lt;a href="http://chanwit.blogspot.com/2007/04/groovy-running-fine-on-net.html">Groovy run on .Net!&lt;/a> This is pretty cool IMHO. He used IKVM for that. You&amp;rsquo;ll have to try it out.&lt;/li>
&lt;li>Alex shows how to use the native Groovy mocks to &lt;a href="http://themindstorms.blogspot.com/2007/04/groovy-mocks.html">mock properties&lt;/a>.&lt;/li>
&lt;li>Andres has created a special &lt;a href="http://www.jroller.com/page/aalmiray/?anchor=live_grapplet_test">Groovy applet&lt;/a> to let you write Groovy code in your script tags in your HTML pages!&lt;/li>
&lt;li>Glen is &lt;a href="http://blogs.bytecode.com.au/glen/2007/04/28/1177707183592.html">having fun&lt;/a> with &lt;a href="http://groovy.codehaus.org/COM+Scripting">Scriptom&lt;/a> (Groovy COM/ActiveX module) to synchronize his &lt;a href="http://www.rememberthemilk.com/">Remember the Milk&lt;/a> todo list, with Outlook.&lt;/li>
&lt;li>Danno is &lt;a href="http://shemnon.com/speling/2007/04/colosures-in-java-too-much-typ.html">comparing the upcoming Java closure notation with Groovy&amp;rsquo;s closure notation&lt;/a>. I&amp;rsquo;m sure you&amp;rsquo;ll easily know which one is the sexiest and how a language that supports weak typing can be more concise and expressive.&lt;/li>
&lt;li>Warner experiments with &lt;a href="http://www.warneronstine.com/blog/articles/2007/04/27/my-groovy-hom-take-2">Higher-Order Messaging&lt;/a> with a Groovy implementation.&lt;/li>
&lt;/ul>
&lt;p>Next week should be pretty interesting too as we&amp;rsquo;re going to release the first beta of &lt;strong>Groovy 1.1&lt;/strong> in time just before &lt;a href="http://java.sun.com/javaone/sf/index.jsp">JavaOne&lt;/a> where there will be a lot of sessions dedicated to &lt;a href="http://www28.cplan.com/cc158/sessions_catalog.jsp?ilc=158-1&amp;amp;ilg=english&amp;amp;isort=&amp;amp;isort_type=&amp;amp;is=yes&amp;amp;icriteria1=+&amp;amp;icriteria2=+&amp;amp;icriteria7=+&amp;amp;icriteria9=&amp;amp;icriteria8=groovy">Groovy&lt;/a> and &lt;a href="http://www28.cplan.com/cc158/sessions_catalog.jsp?ilc=158-1&amp;amp;ilg=english&amp;amp;isort=&amp;amp;isort_type=&amp;amp;is=yes&amp;amp;icriteria1=+&amp;amp;icriteria2=+&amp;amp;icriteria7=+&amp;amp;icriteria9=&amp;amp;icriteria8=grails">Grails&lt;/a>. Groovy will be the first alternative language for the JVM to support some Java 5 features. &lt;strong>Groovy 1.1 supports annotations and static imports&lt;/strong>. If you plan to use another language than Java and leverage other key frameworks using annotations like &lt;a href="http://www.infoq.com/news/2006/12/spring-config">Spring&lt;/a>, &lt;a href="http://www.curious-creature.org/2007/03/25/persistence-made-easy-with-groovy-and-jpa/">EJB 3 / JPA&lt;/a>, &lt;a href="http://groovy.codehaus.org/Using+TestNG+with+Groovy">TestNG&lt;/a> or &lt;a href="http://glaforge.free.fr/weblog/index.php?itemid=208">Guice&lt;/a>, your best option will be Groovy as it will be the sole alternative language supporting those frameworks.&lt;/p></description><content:encoded>
<![CDATA[<p>After <a href="http://glaforge.free.fr/weblog/index.php?itemid=210">Groovy won the JAX 2007 innovation award</a>, I took some time to look at what was going on in the blogosphere. There&rsquo;s always a lot of activity in the Groovy-sphere. It never ceases to amaze me how prolific the community is. Let&rsquo;s list some of the interesting posts I&rsquo;ve come across this week-end.</p>
<ul>
<li>Chanwit managed to make <a href="http://chanwit.blogspot.com/2007/04/groovy-running-fine-on-net.html">Groovy run on .Net!</a> This is pretty cool IMHO. He used IKVM for that. You&rsquo;ll have to try it out.</li>
<li>Alex shows how to use the native Groovy mocks to <a href="http://themindstorms.blogspot.com/2007/04/groovy-mocks.html">mock properties</a>.</li>
<li>Andres has created a special <a href="http://www.jroller.com/page/aalmiray/?anchor=live_grapplet_test">Groovy applet</a> to let you write Groovy code in your script tags in your HTML pages!</li>
<li>Glen is <a href="http://blogs.bytecode.com.au/glen/2007/04/28/1177707183592.html">having fun</a> with <a href="http://groovy.codehaus.org/COM+Scripting">Scriptom</a> (Groovy COM/ActiveX module) to synchronize his <a href="http://www.rememberthemilk.com/">Remember the Milk</a> todo list, with Outlook.</li>
<li>Danno is <a href="http://shemnon.com/speling/2007/04/colosures-in-java-too-much-typ.html">comparing the upcoming Java closure notation with Groovy&rsquo;s closure notation</a>. I&rsquo;m sure you&rsquo;ll easily know which one is the sexiest and how a language that supports weak typing can be more concise and expressive.</li>
<li>Warner experiments with <a href="http://www.warneronstine.com/blog/articles/2007/04/27/my-groovy-hom-take-2">Higher-Order Messaging</a> with a Groovy implementation.</li>
</ul>
<p>Next week should be pretty interesting too as we&rsquo;re going to release the first beta of <strong>Groovy 1.1</strong> in time just before <a href="http://java.sun.com/javaone/sf/index.jsp">JavaOne</a> where there will be a lot of sessions dedicated to <a href="http://www28.cplan.com/cc158/sessions_catalog.jsp?ilc=158-1&amp;ilg=english&amp;isort=&amp;isort_type=&amp;is=yes&amp;icriteria1=+&amp;icriteria2=+&amp;icriteria7=+&amp;icriteria9=&amp;icriteria8=groovy">Groovy</a> and <a href="http://www28.cplan.com/cc158/sessions_catalog.jsp?ilc=158-1&amp;ilg=english&amp;isort=&amp;isort_type=&amp;is=yes&amp;icriteria1=+&amp;icriteria2=+&amp;icriteria7=+&amp;icriteria9=&amp;icriteria8=grails">Grails</a>. Groovy will be the first alternative language for the JVM to support some Java 5 features. <strong>Groovy 1.1 supports annotations and static imports</strong>. If you plan to use another language than Java and leverage other key frameworks using annotations like <a href="http://www.infoq.com/news/2006/12/spring-config">Spring</a>, <a href="http://www.curious-creature.org/2007/03/25/persistence-made-easy-with-groovy-and-jpa/">EJB 3 / JPA</a>, <a href="http://groovy.codehaus.org/Using+TestNG+with+Groovy">TestNG</a> or <a href="http://glaforge.free.fr/weblog/index.php?itemid=208">Guice</a>, your best option will be Groovy as it will be the sole alternative language supporting those frameworks.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy awarded JAX innovation first prize!</title><link>https://glaforge.dev/posts/2007/04/26/groovy-awarded-jax-innovation-first-prize/</link><pubDate>Thu, 26 Apr 2007 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2007/04/26/groovy-awarded-jax-innovation-first-prize/</guid><description>&lt;p>&lt;a href="http://jax.de/konferenzen/jax07/index_eng.php">JAX&lt;/a> is the &lt;strong>most important Java conference in Germany&lt;/strong>. Every year, the organizers are running a &lt;a href="http://jax-award.de/jax_award/index_eng.php">contest&lt;/a> to select the &lt;strong>most innovative and creative projects&lt;/strong>. From&lt;a href="http://jax-award.de/jax_award/proposal_ovw_eng.php">over 40 proposals&lt;/a>, the jury selected only &lt;a href="http://jax-award.de/jax_award/nominierung_eng.php">ten nominees&lt;/a>. Although great projects were selected, like the Matisse GUI builder in NetBeans, or the &lt;a href="http://www.nuxeo.com/">Nuxeo&lt;/a> Enterprise Content Management solution, &lt;strong>&lt;a href="http://jax-award.de/jax_award/gewinner_eng.php">Groovy won the first prize&lt;/a>&lt;/strong>! It is a great honor and a huge pleasure for us to receive such a prize, especially knowing the cool projects we were competing with, or the &lt;a href="http://jax-award.de/jax_award06/gewinner_en.php">past winners&lt;/a> like the Spring framework.&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://jax.de/konferenzen/jax07/index_eng.php">JAX</a> is the <strong>most important Java conference in Germany</strong>. Every year, the organizers are running a <a href="http://jax-award.de/jax_award/index_eng.php">contest</a> to select the <strong>most innovative and creative projects</strong>. From<a href="http://jax-award.de/jax_award/proposal_ovw_eng.php">over 40 proposals</a>, the jury selected only <a href="http://jax-award.de/jax_award/nominierung_eng.php">ten nominees</a>. Although great projects were selected, like the Matisse GUI builder in NetBeans, or the <a href="http://www.nuxeo.com/">Nuxeo</a> Enterprise Content Management solution, <strong><a href="http://jax-award.de/jax_award/gewinner_eng.php">Groovy won the first prize</a></strong>! It is a great honor and a huge pleasure for us to receive such a prize, especially knowing the cool projects we were competing with, or the <a href="http://jax-award.de/jax_award06/gewinner_en.php">past winners</a> like the Spring framework.</p>
<p><a href="http://www.amazon.com/gp/blog/A368TUB0Q1IE3F">Dierk König</a>, author of the best-selling &ldquo;<a href="http://groovy.canoo.com/gina">Groovy in Action</a>&rdquo; book, received the prize on behalf of the Groovy community, after having presented several sessions on Groovy at this conference. Dierk took a <a href="http://farm1.static.flickr.com/188/473237227_f497cadb73.jpg?v=0">picture of the prize</a> if you want to see what it looks like.</p>
<p><strong>This award proves how innovative, creative and influential the Groovy project is for the Java community</strong>. After a <strong>1.0 release</strong> this year, and a <strong>book</strong>, with IDE makers working on <strong>IDE plugin support</strong> for the language, with many <strong>companies betting on Groovy</strong> for writing business rules or for scripting their products, with dedicated <a href="http://aboutgroovy.com/">news sites</a> and <a href="http://www.groovyblogs.org/">feed aggregators</a>, with <a href="http://www.grails-exchange.com/">dedicated conferences</a> and tracks, and with over <a href="http://www28.cplan.com/cc158/sessions_catalog.jsp?ilc=158-1&amp;ilg=english&amp;isort=&amp;isort_type=&amp;is=yes&amp;icriteria1=+&amp;icriteria2=+&amp;icriteria7=+&amp;icriteria9=&amp;icriteria8=groovy">10 sessions</a> about <a href="http://www28.cplan.com/cc158/sessions_catalog.jsp?ilc=158-1&amp;ilg=english&amp;isort=&amp;isort_type=&amp;is=yes&amp;icriteria1=+&amp;icriteria2=+&amp;icriteria7=+&amp;icriteria9=&amp;icriteria8=groovy">Groovy</a> and <a href="http://www28.cplan.com/cc158/sessions_catalog.jsp?ilc=158-1&amp;ilg=english&amp;isort=&amp;isort_type=&amp;is=yes&amp;icriteria1=+&amp;icriteria2=+&amp;icriteria7=+&amp;icriteria9=&amp;icriteria8=grails">Grails</a> at the upcoming <a href="http://java.sun.com/javaone/sf/index.jsp">JavaOne</a> conference, Groovy stands out of the crowd and proves it&rsquo;s a very <strong>successful and mature project</strong>.</p>
<p>I would like to seize this opportunity to thank all the Groovy committers and contributors who helped develop this project, as well as the whole Groovy community without which Groovy wouldn&rsquo;t be as great and as cool as it is today. This award is really to all of you, and you&rsquo;re all part of this incredible success.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Guicy: a Groovy Guice?</title><link>https://glaforge.dev/posts/2007/03/26/guicy-a-groovy-guice/</link><pubDate>Mon, 26 Mar 2007 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2007/03/26/guicy-a-groovy-guice/</guid><description>&lt;p>&lt;figure>
&lt;a href="#img-b3d2d864adaf1f5737af18cc37e6cbd8">
&lt;img src="http://crazybob.org/uploaded_images/juice-770770.gif"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-b3d2d864adaf1f5737af18cc37e6cbd8">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="http://crazybob.org/uploaded_images/juice-770770.gif"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
I recently came across &lt;a href="http://crazybob.org/">Bob Lee&lt;/a>&amp;rsquo;s brand new IoC/DI framework: &lt;a href="http://code.google.com/p/google-guice/">Guice&lt;/a>. I&amp;rsquo;m usually using &lt;a href="http://www.springframework.org/">Spring&lt;/a> for that purpose, and also because it goes much farther than just IoC/DI, but I thought I&amp;rsquo;d give Guice a try, especially because I wanted to play with &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a>&amp;rsquo;s support for annotations. So I &lt;a href="http://google-guice.googlecode.com/files/guice-1.0.zip">downloaded Guice&lt;/a>, and read the nice getting started&lt;a href="http://docs.google.com/Doc?id=dd2fhx4z_5df5hw8">documentation&lt;/a> I also took a snapshot of Groovy 1.1 that supports Java 5 annotations. With guice-1.0.jar and aopalliance.jar on my classpath, and with the latest Groovy snapshot distribution properly installed, I was ready to go!&lt;/p></description><content:encoded>
<![CDATA[<p><figure>
  <a href="#img-b3d2d864adaf1f5737af18cc37e6cbd8">
    <img src="http://crazybob.org/uploaded_images/juice-770770.gif"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-b3d2d864adaf1f5737af18cc37e6cbd8">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="http://crazybob.org/uploaded_images/juice-770770.gif"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
I recently came across <a href="http://crazybob.org/">Bob Lee</a>&rsquo;s brand new IoC/DI framework: <a href="http://code.google.com/p/google-guice/">Guice</a>. I&rsquo;m usually using <a href="http://www.springframework.org/">Spring</a> for that purpose, and also because it goes much farther than just IoC/DI, but I thought I&rsquo;d give Guice a try, especially because I wanted to play with <a href="http://groovy.codehaus.org/">Groovy</a>&rsquo;s support for annotations. So I <a href="http://google-guice.googlecode.com/files/guice-1.0.zip">downloaded Guice</a>, and read the nice getting started<a href="http://docs.google.com/Doc?id=dd2fhx4z_5df5hw8">documentation</a> I also took a snapshot of Groovy 1.1 that supports Java 5 annotations. With guice-1.0.jar and aopalliance.jar on my classpath, and with the latest Groovy snapshot distribution properly installed, I was ready to go!</p>
<p>So, how do we start? Well, first of all, you must have some service contract that you&rsquo;d like to depend on and to inject in some client code. Nothing really fancy here, I just shamelessly took inspiration from the documentation:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">interface</span> <span style="color:#0e84b5;font-weight:bold">Service</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">void</span> <span style="color:#06287e">go</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Now, I need a concrete implementation of this service:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">ServiceImpl</span> <span style="color:#007020;font-weight:bold">implements</span> Service <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">void</span> <span style="color:#06287e">go</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        println <span style="color:#4070a0">&#34;Okay, I&#39;m going somewhere&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>So far so good, now, we&rsquo;ll need some client code that needs a service to be injected. This is where you&rsquo;re going to see some specific juicy annotation coming into play.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">ClientWithCtor</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">private</span> <span style="color:#007020;font-weight:bold">final</span> Service service
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Inject</span>
</span></span><span style="display:flex;"><span>    Client<span style="color:#666">(</span>Service service<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">this</span><span style="color:#666">.</span><span style="color:#4070a0">service</span> <span style="color:#666">=</span> service
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">void</span> <span style="color:#06287e">go</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        service<span style="color:#666">.</span><span style="color:#4070a0">go</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Here, I&rsquo;m creating a client class where I&rsquo;m using constructor injection. The sole thing we have to do here is just use the <code>@Inject</code> annotation. But of course, so far, the wiring isn&rsquo;t specified anywhere, and we have to do it now. Guice has the concept of Modules which contain programmatic code to wire classes together.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">MyModule</span> <span style="color:#007020;font-weight:bold">implements</span> Module <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">void</span> <span style="color:#06287e">configure</span><span style="color:#666">(</span>Binder binder<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        binder<span style="color:#666">.</span><span style="color:#4070a0">bind</span><span style="color:#666">(</span>Service<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>            <span style="color:#666">.</span><span style="color:#4070a0">to</span><span style="color:#666">(</span>ServiceImpl<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>            <span style="color:#666">.</span><span style="color:#4070a0">in</span><span style="color:#666">(</span>Scopes<span style="color:#666">.</span><span style="color:#4070a0">SINGLETON</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>We are binding the <code>Service</code> interface to the <code>ServiceImpl</code> implementation. And we also mention the scope of the injection: we want to have one single implementation of that service available. Instead of specifying the scope in the module, you could also use a <code>@Singleton</code> annotation on the <code>ServiceImpl</code> class.</p>
<p>Now that everything is in place, we can create a Guice&rsquo;s injector and retrieve and call a properly wired client with the following code:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> injector <span style="color:#666">=</span> Guice<span style="color:#666">.</span><span style="color:#4070a0">createInjector</span><span style="color:#666">(</span><span style="color:#007020;font-weight:bold">new</span> MyModule<span style="color:#666">())</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> clientWithCtor <span style="color:#666">=</span> injector<span style="color:#666">.</span><span style="color:#4070a0">getInstance</span><span style="color:#666">(</span>ClientWithCtor<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>clientWithCtor<span style="color:#666">.</span><span style="color:#4070a0">go</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>Instead of the constructor-based approach, I prefer using a setter-based approch. And since Groovy creates getters and setters automagically when you define a property, the code is a bit shorter:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">ClientWithSetter</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Inject</span> Service service
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">void</span> <span style="color:#06287e">go</span><span style="color:#666">()</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        service<span style="color:#666">.</span><span style="color:#4070a0">go</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>And the code for injecting is still the same:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> clientWithSetter <span style="color:#666">=</span> injector<span style="color:#666">.</span><span style="color:#4070a0">getInstance</span><span style="color:#666">(</span>ClientWithSetter<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>clientWithSetter<span style="color:#666">.</span><span style="color:#4070a0">go</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>I&rsquo;m not sure I&rsquo;d use Guice for a customer project anytime soon, but for small projects where I want a xml-free DI framework, that might do! However, I might be tempted to use Grails&rsquo; <a href="http://grails.org/Spring+Bean+Builder">Spring bean builder</a>instead, since it&rsquo;s a pretty cool way to avoid the usual XML-hell when working with Spring. Also, in conclusion, it seems that Groovy&rsquo;s new support for annotations work quite well, as demonstrated also by Romain while <a href="http://www.curious-creature.org/2007/03/25/persistence-made-easy-with-groovy-and-jpa/">integrating Groovy and JPA</a>. I&rsquo;m sure this will propel <strong>Groovy as the de facto enterprise scripting solution</strong> leveraging the wealth of frameworks and libraries using annotations.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy and Grails news, conferences and IDE support</title><link>https://glaforge.dev/posts/2007/03/12/groovy-and-grails-news-conferences-and-ide-support/</link><pubDate>Mon, 12 Mar 2007 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2007/03/12/groovy-and-grails-news-conferences-and-ide-support/</guid><description>&lt;p>As always, lots of great things are happening in the &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> and &lt;a href="http://grails.org/">Grails&lt;/a> community. If you&amp;rsquo;d like to stay up-to-date with the news, but if you don&amp;rsquo;t want to spend the whole day reading our high-traffic &lt;a href="http://www.nabble.com/codehaus---Groovy-f11866.html">mailing-lists&lt;/a>, you should certainly consider subscribing to one of these two resources:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://www.aboutgroovy.com/">AboutGroovy&lt;/a>: The community portal news site about everything Groovy and Grails, with frequent news items, podcast interviews, pointers to important resources.&lt;/li>
&lt;li>&lt;a href="http://www.groovyblogs.org/">GroovyBlogs&lt;/a>: A JavaBlog-like news agregator agregating the Grooyv and Grails mailing-lists feeds, and many feeds from famous bloggers spreading the Groovy and Grails love.&lt;/li>
&lt;/ul>
&lt;p>Both of these resources have been developed in Grails! &lt;a href="http://www.groovyblogs.org/">GroovyBlogs&lt;/a> has even been developed in &lt;a href="http://blogs.bytecode.com.au/glen/2007/02/27/1172521648562.html">under 24 hours by Glen Smith&lt;/a>!&lt;/p></description><content:encoded>
<![CDATA[<p>As always, lots of great things are happening in the <a href="http://groovy.codehaus.org/">Groovy</a> and <a href="http://grails.org/">Grails</a> community. If you&rsquo;d like to stay up-to-date with the news, but if you don&rsquo;t want to spend the whole day reading our high-traffic <a href="http://www.nabble.com/codehaus---Groovy-f11866.html">mailing-lists</a>, you should certainly consider subscribing to one of these two resources:</p>
<ul>
<li><a href="http://www.aboutgroovy.com/">AboutGroovy</a>: The community portal news site about everything Groovy and Grails, with frequent news items, podcast interviews, pointers to important resources.</li>
<li><a href="http://www.groovyblogs.org/">GroovyBlogs</a>: A JavaBlog-like news agregator agregating the Grooyv and Grails mailing-lists feeds, and many feeds from famous bloggers spreading the Groovy and Grails love.</li>
</ul>
<p>Both of these resources have been developed in Grails! <a href="http://www.groovyblogs.org/">GroovyBlogs</a> has even been developed in <a href="http://blogs.bytecode.com.au/glen/2007/02/27/1172521648562.html">under 24 hours by Glen Smith</a>!</p>
<h2 id="conferences">Conferences</h2>
<p>I also wanted to say a few words about upcoming conference events.</p>
<ul>
<li>
<p>A preliminary search on <a href="http://java.sun.com/javaone/sf/index.jsp">JavaOne</a>&rsquo;s online <a href="http://www.cplan.com/javaone2007/contentcatalog/">catalog</a> shows no less than 8 sessions mentioning Groovy! It&rsquo;s alreday two more sessions than last year&rsquo;s JavaOne! Of higher important to me, of course, are the two sessions I&rsquo;m involved with:</p>
<ul>
<li><strong>TS-1742: Cool things you can do with the Groovy programming language</strong>, with Guillaume Alléon and Dierk Koenig.</li>
<li><strong>BOF-6133: Grails: Spring + Hibernate development re-invented</strong>, with Graeme Rocher.</li>
</ul>
<p>So don&rsquo;t forget to come to JavaOne and hear about the latest news about Groovy and Grails.</p>
</li>
<li>
<p>Now, for a shorter-term aspect, let me mention I&rsquo;ll be at <a href="http://qcon.infoq.com/qcon/">QCon</a> this week, in London. I&rsquo;ll be happy to present two sessions:</p>
<ul>
<li>
<p><a href="http://qcon.infoq.com/qcon/speakers/show_speaker.jsp?oid=154">Implementing Domain-Specific Languages in Groovy</a>: Johwn Wilson and myself will be explaining how to create DSLs with the help of Groovy. That&rsquo;s an area where Groovy particularly shines, thanks to its maleable syntax and its natural expressivity.</p>
</li>
<li>
<p><a href="http://qcon.infoq.com/qcon/speakers/show_speaker.jsp?oid=154">Grails: Spring + Hibernate development re-invented</a>: As Graeme Rocher will be at <a href="http://uk.sun.com/sunnews/events/2007/mar/revolution/techdays07/grails.html">Sun&rsquo;s TechDays in London</a>, he couldn&rsquo;t do the two sessions in parallel, so I&rsquo;m replacing Graeme, and will be explaining how Grails simplifies the development of Web applications.</p>
</li>
</ul>
</li>
<li>
<p><a href="http://fr.sun.com/sunnews/events/2007/mar/techdays/">Sun TechDays in Paris</a>: on March 20th/21st, I&rsquo;ll be on stage at Sun&rsquo;s Parisian TechDays worldwide tour to speak about Groovy as an <a href="http://fr.sun.com/sunnews/events/2007/mar/techdays/agenda.jsp#agenda-2">alternative language for the JVM</a>. Like last year, James Gosling will be present! And I still recall the pleasure I felt when <a href="http://glaforge.free.fr/weblog/index.php?itemid=185&amp;catid=2">Gosling told me he&rsquo;d used Groovy on a couple of internal projects at Sun</a> and said he enjoyed it a lot! That&rsquo;s also what he has written in his foreword of our book: <a href="http://groovy.canoo.com/gina">Groovy in Action</a>. Speaking of &ldquo;GinA&rdquo;, I also encourage you to <a href="http://books.slashdot.org/article.pl?sid=07/02/28/1435250">read Slashdot&rsquo;s review</a>.</p>
</li>
<li>
<p>Last but not least, the much awaited <a href="http://www.grails-exchange.com/">Grails eXchange conference</a> will take place in London at the end of May: a dedicated conference to Groovy and Grails! This is the first event of this kind, but more are to be expected, both in the US and in Europe! An incredible line of renown speakers have already signed in: Rod Johnson, Joe Walker, Rob Harrop, Rod Cope, Dierk Koenig, Joe Walnes, etc&hellip; This is your best opportunity to meet all the guys who make Groovy and Grails, and to hear the latest tricks from famous Java champions.</p>
</li>
</ul>
<h2 id="groovy-ide-plugins">Groovy IDE Plugins</h2>
<p>Some additional Groovy news: the complaint we often hear about is the lack of tooling support. Groovy has had basic plugins for all the major IDEs and text editors, but this is not sufficient. Fortunately, IDE support is high on our list for 2007, and I can tell you there are some great things to come on that front:</p>
<ul>
<li>
<p>First of all, the <a href="http://groovy.codehaus.org/Eclipse+Plugin">Eclipse plugin</a> is making great progress, and code-completion is not very far. You should read Ed&rsquo;s blog to see <a href="http://www.rippleinteractive.com/blog/2007/03/07/1173331980000.html">some nice screenshots</a> of the completion he&rsquo;s working on this month.</p>
</li>
<li>
<p>Some guys at Sun are also investigating how to develop and <a href="http://cld.blog-city.com/grails_on_netbeans__a_project_in_the_making.htm">Grails IDE for NetBeans</a>. It could be based on the stagnant <a href="http://groovy.codehaus.org/NetBeans+Plugin">Coyote Groovy plugin</a>, and would provide wizards for Grails artifacts, as well as all the Groovy support that&rsquo;s needed.</p>
</li>
<li>
<p>My <a href="http://www.jetbrains.com/idea/">favorite Java IDE</a> is not forgotten either, since <a href="http://www.jetbrains.com/">JetBrains</a> is investigating how to retrive the <a href="http://groovy.codehaus.org/IntelliJ+IDEA+Plugin">GroovyJ</a> plugin and to make it work as good as they made their JavaScript or Ruby integrations.</p>
</li>
</ul>
<p>So you can expect a very high and good support for Groovy and Grails in the mainstream IDEs!</p>
<p>As I said, a lot is happening in the Groovy and Grails community, it&rsquo;s hard to follow everything. 2007 is really the year of Groovy and Grails!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>New version of the Groovy Eclipse Plugin</title><link>https://glaforge.dev/posts/2007/01/12/new-version-of-the-groovy-eclipse-plugin/</link><pubDate>Fri, 12 Jan 2007 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2007/01/12/new-version-of-the-groovy-eclipse-plugin/</guid><description>&lt;p>Scott Hickey, the project lead of the &lt;a href="http://groovy.codehaus.org/Eclipse+Plugin">Groovy Eclipse plugin&lt;/a> has just announced the availability of the plugin which now uses the offcial &lt;a href="http://glaforge.free.fr/weblog/index.php?itemid=200">Groovy 1.0 release&lt;/a>. The details of the announcement are reproduced below. There&amp;rsquo;s even some **basic code completion **available!&lt;/p>
&lt;p>Recently, EclipseZone proposed a &lt;a href="http://www.eclipsezone.com/eclipse/forums/t88129.rhtml">Getting Started article&lt;/a> explaining the basic usage of the Groovy plugin.&lt;/p>
&lt;blockquote>
&lt;p>There is a new version of the Groovy Eclipse Plugin available on the update site. For installation instructions, please see the wiki page &lt;a href="http://groovy.codehaus.org/Eclipse+Plugin">http://groovy.codehaus.org/Eclipse+Plugin&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>Scott Hickey, the project lead of the <a href="http://groovy.codehaus.org/Eclipse+Plugin">Groovy Eclipse plugin</a> has just announced the availability of the plugin which now uses the offcial <a href="http://glaforge.free.fr/weblog/index.php?itemid=200">Groovy 1.0 release</a>. The details of the announcement are reproduced below. There&rsquo;s even some **basic code completion **available!</p>
<p>Recently, EclipseZone proposed a <a href="http://www.eclipsezone.com/eclipse/forums/t88129.rhtml">Getting Started article</a> explaining the basic usage of the Groovy plugin.</p>
<blockquote>
<p>There is a new version of the Groovy Eclipse Plugin available on the update site. For installation instructions, please see the wiki page <a href="http://groovy.codehaus.org/Eclipse+Plugin">http://groovy.codehaus.org/Eclipse+Plugin</a>.</p>
<p>This version of the plugin supports Groovy 1.0. Thanks to everybody who made Groovy 1.0 possible.</p>
<p>Please make sure to update the Eclipse project build path for any existing projects to use Groovy 1.0. For the plugin to function correctly, it is important the version of Groovy used by the plugin and the version included in the build path are the same.</p>
<p>This version of the plugin features:</p>
<ul>
<li>Run as Groovy now only shows up on Groovy files that are executable instead of all Groovy files</li>
<li>Run as Junit is now available for Groovy unit tests, which will display the standard Eclipse Junit view. Run as Groovy for Junit tests will still display inthe console view.</li>
<li>Run as Groovy and Run as JUnit now create Groovy Launch configurations, which will also show up in the Launch history. This enables the run last launched toolbar button to work.</li>
<li>Alt-Shift-X-G runs a Groovy file, also creating a Launch Configuration</li>
<li>Alt-Shift-X-D starts the debugger, also creating a Launch Configuration</li>
<li>Popup menu on Projects now has options to launch the Groovy Console in a separate window or the Groovy console in the standard Eclipse console. Both are launched with the project classpath.</li>
<li>Limited <strong>code assist</strong>, for methods and variables defined in the same file and Default Groovy Methods</li>
<li>Going forward, plugin updates should be picked up automatically with &ldquo;Search for updates&hellip;&rdquo; instead of &ldquo;Search for new features&rdquo;</li>
</ul>
<p>The plugin has come a long ways over the last nine months. It wouldn&rsquo;t have been possible without the tremendous effort put forth by Edward Povazan, James Ervin, and David Kerber. Thanks you guys!</p>
<p>Scott</p></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy 1.0 is there!</title><link>https://glaforge.dev/posts/2007/01/02/groovy-1-0-is-there/</link><pubDate>Tue, 02 Jan 2007 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2007/01/02/groovy-1-0-is-there/</guid><description>&lt;p>The Groovy developer team and myself are proud and delighted to announce the &lt;strong>final release of Groovy 1.0&lt;/strong>.&lt;/p>
&lt;p>Groovy is a &lt;strong>dynamic language for the JVM that integrates seamlessly with the Java platform&lt;/strong>.&lt;br />
It offers a &lt;strong>Java-like syntax&lt;/strong>, with language features inspired by Smalltalk, Python or Ruby, and lets your reuse all your Java libraries and &lt;strong>protect the investment you made in Java skills, tools or application servers&lt;/strong>.&lt;br />
Groovy can be used for various purposes, from adhoc shell scripting leveraging Java APIs, to full-blown web applications built on Spring and Hibernate through the &lt;a href="http://grails.org/">Grails&lt;/a> web framework.&lt;br />
It can also be integrated very easily in your applications to externalize business logic, create &lt;a href="http://en.wikipedia.org/wiki/Domain-specific_programming_language">Domain-Specific Languages&lt;/a>, or to provide templating capabilities, and much more.&lt;/p></description><content:encoded>
<![CDATA[<p>The Groovy developer team and myself are proud and delighted to announce the <strong>final release of Groovy 1.0</strong>.</p>
<p>Groovy is a <strong>dynamic language for the JVM that integrates seamlessly with the Java platform</strong>.<br />
It offers a <strong>Java-like syntax</strong>, with language features inspired by Smalltalk, Python or Ruby, and lets your reuse all your Java libraries and <strong>protect the investment you made in Java skills, tools or application servers</strong>.<br />
Groovy can be used for various purposes, from adhoc shell scripting leveraging Java APIs, to full-blown web applications built on Spring and Hibernate through the <a href="http://grails.org/">Grails</a> web framework.<br />
It can also be integrated very easily in your applications to externalize business logic, create <a href="http://en.wikipedia.org/wiki/Domain-specific_programming_language">Domain-Specific Languages</a>, or to provide templating capabilities, and much more.</p>
<p>A lot of passion and energy has been put in this new version after <strong>two release candidates that have been tested against real-world projects</strong>: on a <strong>mission-critical insurance application</strong>, on the <a href="http://www.xwiki.org/xwiki/bin/view/Main/WebHome">XWiki</a>2nd generation wiki engine, as well as on the <a href="http://rifers.org/">RIFE</a> framework and through the <a href="http://springframework.org/">Spring 2.0</a> scripting integration.</p>
<p>Groovy 1.0&rsquo;s distribution can be downloaded from the usual place:<br />
<a href="http://dist.codehaus.org/groovy/distributions/?C=M;O=D">http://dist.codehaus.org/groovy/distributions/</a></p>
<p>The JARs should be in Maven&rsquo;s repositories soon, but in the meantime, you can find them here:<br />
<a href="http://dist.codehaus.org/groovy/jars/?C=M;O=D">http://dist.codehaus.org/groovy/jars/</a></p>
<p>We hope you will enjoy this release as much as we enjoyed working on it.<br />
Let me thank the whole Groovy team and all the past Groovy committers for their hard work &ndash; too many persons to name everybody!<br />
Big thanks to all our users who have been helping us spot bugs, answer questions on the lists, or contribute documentation and ideas.<br />
It&rsquo;s been a wonderful experience to work with you all to deliver this important milestone of the Groovy project.</p>
<p>On behalf of the whole team, I also seize this opportunity to wish you <strong>all the best for this new year</strong>.<br />
<strong>May 2007 be a groovy year</strong>! If you haven&rsquo;t used Groovy yet, make sure that learning Groovy is on your list of resolutions for 2007!<br />
And if that&rsquo;s the case, and you wish to learn Groovy or to dive deeper, you should certainly read &ldquo;<strong><a href="http://groovy.canoo.com/gina"><strong>Groovy in Action</strong></a></strong>&rdquo;, <strong><em>the</em></strong> Groovy reference book published by Manning.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy development funding</title><link>https://glaforge.dev/posts/2006/12/21/groovy-development-funding/</link><pubDate>Thu, 21 Dec 2006 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2006/12/21/groovy-development-funding/</guid><description>&lt;p>The news is on the streets, on eWeek: &lt;a href="http://www.eweek.com/article2/0,1895,2074908,00.asp">Groovy gets funding for its development&lt;/a>, thanks to Big Sky hiring the most prolific Groovy commiter: &lt;a href="http://blackdragsview.blogspot.com/">Jochen Theodorou&lt;/a>. Big Sky is the company behind the &lt;a href="http://www.nofluffjuststuff.com/index.jsp">No Fluff Just Stuff symposium tour&lt;/a> and for their 2007 tour, they will offer a dedicated Groovy and Grails track!&lt;/p>
&lt;p>&lt;a href="http://www.infoq.com/news/2006/12/groovy-sponsorship">InfoQ&lt;/a> also features a &lt;a href="http://www.infoq.com/news/2006/12/groovy-sponsorship">nice article&lt;/a> with Jay Zimmerman and Jochen Theodorou&amp;rsquo;s interviews.&lt;/p>
&lt;p>In the press release, Jay Zimmerman, the founder of Big Sky had this to say:&lt;/p></description><content:encoded>
<![CDATA[<p>The news is on the streets, on eWeek: <a href="http://www.eweek.com/article2/0,1895,2074908,00.asp">Groovy gets funding for its development</a>, thanks to Big Sky hiring the most prolific Groovy commiter: <a href="http://blackdragsview.blogspot.com/">Jochen Theodorou</a>. Big Sky is the company behind the <a href="http://www.nofluffjuststuff.com/index.jsp">No Fluff Just Stuff symposium tour</a> and for their 2007 tour, they will offer a dedicated Groovy and Grails track!</p>
<p><a href="http://www.infoq.com/news/2006/12/groovy-sponsorship">InfoQ</a> also features a <a href="http://www.infoq.com/news/2006/12/groovy-sponsorship">nice article</a> with Jay Zimmerman and Jochen Theodorou&rsquo;s interviews.</p>
<p>In the press release, Jay Zimmerman, the founder of Big Sky had this to say:</p>
<blockquote>
<p>The move will help ensure that &ldquo;2007 will be remembered in the Java community as the year the Groovy revolution started,&rdquo; Zimmerman said. &ldquo;With more than 4 million Java developers worldwide, we see a very bright and robust future for Groovy.&rdquo;</p></blockquote>
<p>I also firmly believe that 2007 will be a milestone in the life of the project and we&rsquo;ll see an amazing adoption rate increase for both <a href="http://groovy.codehaus.org/">Groovy</a> and <a href="http://grails.org/">Grails</a>. Having a full-time commiter on Groovy will definitely help the project move at a much faster pace.</p>
<p>There&rsquo;s an incredible convergence going around Groovy and Grails these days:</p>
<ul>
<li>Releases of Groovy 1.0 and Grails 0.4</li>
<li>A full-time commiter on Groovy</li>
<li>Two <a href="http://groovy.canoo.com/gina">books on Groovy</a> and one on <a href="http://www.amazon.com/Definitive-Guide-Grails/dp/1590597583/sr=8-2/qid=1165386946?ie=UTF8&amp;s=books&amp;tag2=abougroo-20">Grails</a>.</li>
<li>A dedicated Groovy and Grails website: <a href="http://aboutgroovy.com/">aboutGroovy.com</a></li>
<li>Also a dedicated Groovy and Grails conference: the <a href="http://skillsmatter.com/grailsexchange">Grails eXchange 2007</a></li>
<li>And we&rsquo;re going to hold our third Groovy Developer Conference in Paris at the end of January to pave the roadmap of Groovy 2.0.</li>
</ul>
<p>Man, that&rsquo;s going to be a Groovy and busy year!</p>
<p>I&rsquo;m so happy that my friend Jochen can now spend his time on Groovy! Through his hard work, Groovy improved drastically over the years, and has become a widely acclaimed platform of choice for companies needing a powerful dynamic language with the best Java integration possible. Congratulations, my friend!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy and Grails community site launching...</title><link>https://glaforge.dev/posts/2006/12/06/groovy-and-grails-community-site-launching/</link><pubDate>Wed, 06 Dec 2006 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2006/12/06/groovy-and-grails-community-site-launching/</guid><description>&lt;p>All about &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> and &lt;a href="http://grails.org/">Grails&lt;/a> on a dedicated community news site! Soon to be launched!&lt;/p>
&lt;p>Yes, you read it. There&amp;rsquo;s going to be a site devoted to everything &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> and Grails! The news is unleashed, on the streets. At the time of this writing, the site isn&amp;rsquo;t up yet, but there&amp;rsquo;s a countdown telling us there&amp;rsquo;s still 1 day and 16 hours left before &lt;a href="http://aboutgroovy.com/">http://aboutgroovy.com&lt;/a> is open. It will be online just before the start of the &lt;a href="http://www.thespringexperience.com/speaker_view.jsp?speakerId=728">Spring Experience conference&lt;/a> taking place in Hollywood, Florida. I&amp;rsquo;ll be speaking there by the way: I&amp;rsquo;ll be presenting &lt;a href="http://grails.org/">Grails&lt;/a> and along with Rod Johnson, we&amp;rsquo;ll explain how integrating Spring and &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> together.&lt;/p></description><content:encoded>
<![CDATA[<p>All about <a href="http://groovy.codehaus.org/">Groovy</a> and <a href="http://grails.org/">Grails</a> on a dedicated community news site! Soon to be launched!</p>
<p>Yes, you read it. There&rsquo;s going to be a site devoted to everything <a href="http://groovy.codehaus.org/">Groovy</a> and Grails! The news is unleashed, on the streets. At the time of this writing, the site isn&rsquo;t up yet, but there&rsquo;s a countdown telling us there&rsquo;s still 1 day and 16 hours left before <a href="http://aboutgroovy.com/">http://aboutgroovy.com</a> is open. It will be online just before the start of the <a href="http://www.thespringexperience.com/speaker_view.jsp?speakerId=728">Spring Experience conference</a> taking place in Hollywood, Florida. I&rsquo;ll be speaking there by the way: I&rsquo;ll be presenting <a href="http://grails.org/">Grails</a> and along with Rod Johnson, we&rsquo;ll explain how integrating Spring and <a href="http://groovy.codehaus.org/">Groovy</a> together.</p>
<p>On the Groovy mailing-lists, Scott Davis spoke a bit about <a href="http://aboutgroovy.com/">http://aboutgroovy.com</a> saying it&rsquo;ll be a dedicated news site, much like Digg, <a href="http://www.infoq.com/groovy">InfoQ</a> or Slashdot.</p>
<p>Scott told us that this site will:</p>
<ul>
<li>spotlight strong blogs like <a href="http://codeforfun.wordpress.com/">CodeForFun</a> or <a href="http://jroller.com/page/habuma/20061127">Craig Walls</a> &ndash; of Spring in Action fame</li>
<li>cover corporate articles like the ones from <a href="http://www.oracle.com/technology/pub/articles/grall-grails.html">Oracle</a> and <a href="http://dev2dev.bea.com/pub/a/2006/10/introduction-groovy-grails.html">BEA</a></li>
<li>publish tutorials (Scott Hickey has a nice three article series on bootstrapping a Groovy+Java project with Ant, JUnit, and <a href="http://groovy.codehaus.org/Code+Coverage+with+Cobertura">Cobertura</a>)</li>
<li>PDF chapters from Graeme&rsquo;s excellent book: <a href="http://www.amazon.co.uk/Definitive-Guide-Grails-Graeme-Rocher/dp/1590597583">The Definitive Guide to Grails</a>, and the equally excellent <a href="http://www.amazon.co.uk/Groovy-Action-Dierk-Konig/dp/1932394842/ref=pd_bxgy_b_text_b/026-7604440-4591668">Groovy in Action</a></li>
<li>offer links to screen casts, podcasts, and video interviews</li>
</ul>
<p>If you&rsquo;re speaking at a JUG, or if you are working for a company that uses Groovy/Grails extensively and would like to be featured in a case study, they&rsquo;ll be happy to interview you. You&rsquo;ll also be able to submit links, write articles, tips&rsquo;n tricks, cool hacks, you name it.</p>
<p>Icing on the cake: <a href="http://aboutgroovy.com/">http://aboutgroovy.com</a> is built on Grails 0.3.1! As Scott told us, they&rsquo;re drinking the kool-aid, and it seems pretty tasty!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>InfoQ covers the release of RC-1 and interviews me</title><link>https://glaforge.dev/posts/2006/12/05/infoq-covers-the-release-of-rc-1-and-interviews-me/</link><pubDate>Tue, 05 Dec 2006 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2006/12/05/infoq-covers-the-release-of-rc-1-and-interviews-me/</guid><description>&lt;p>&lt;a href="http://www.infoq.com/">InfoQ&lt;/a>, the wonderful news site that tracks innovation and change in the enterprise community, has &lt;a href="http://www.infoq.com/news/2006/12/groovy-rc1">just covered the release of Groovy RC-1&lt;/a>. Scott Delap, one of InfoQ&amp;rsquo;s editors, also seized the opportunity to&lt;a href="http://www.infoq.com/news/2006/12/groovy-rc1">interview myself&lt;/a> about this release, the situation of the project, present and future.&lt;/p>
&lt;p>The &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> dynamic language and its &lt;a href="http://grails.org/">Grails&lt;/a> web framework based on proven and scalable OSS components face a nice success in situations where language expressivity matters, and where rapid development time and compatibility with the Java platform are key to answering customers needs. Groovy and Grails are used for applications ranging from risk computation for insurance companies to Grid Computing solutions for aerospace companies.&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://www.infoq.com/">InfoQ</a>, the wonderful news site that tracks innovation and change in the enterprise community, has <a href="http://www.infoq.com/news/2006/12/groovy-rc1">just covered the release of Groovy RC-1</a>. Scott Delap, one of InfoQ&rsquo;s editors, also seized the opportunity to<a href="http://www.infoq.com/news/2006/12/groovy-rc1">interview myself</a> about this release, the situation of the project, present and future.</p>
<p>The <a href="http://groovy.codehaus.org/">Groovy</a> dynamic language and its <a href="http://grails.org/">Grails</a> web framework based on proven and scalable OSS components face a nice success in situations where language expressivity matters, and where rapid development time and compatibility with the Java platform are key to answering customers needs. Groovy and Grails are used for applications ranging from risk computation for insurance companies to Grid Computing solutions for aerospace companies.</p>
<p>This new version slightly improves the overall performance of the platform and proposes a few improvements to the core engine for better compatibility and integration with Java. A good number of bugs have also been fixed while new small refinements have been added to further raise the bar of dynamic capabilities and expressiveness.</p>
<p>I&rsquo;ll be presenting two sessions at the <a href="http://www.thespringexperience.com/">Spring Experience conference</a> in Florida at the end of the week:</p>
<ul>
<li>I&rsquo;ll be on stage with Rod Johnson to show how well <a href="http://www.thespringexperience.com/speaker_topic_view.jsp?topicId=222">Groovy and Spring integrates together</a></li>
<li>and I&rsquo;ll also be <a href="http://www.thespringexperience.com/speaker_topic_view.jsp?topicId=263">introducing Grails</a>, a Groovy-based web framework which relies on Spring under the hood</li>
</ul>
<p>Please come and say hi if you&rsquo;re around. Also, stay tuned for further info, I might tell you some other very good news at the end of the week!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>We've just released Groovy RC-1!</title><link>https://glaforge.dev/posts/2006/12/04/we-ve-just-released-groovy-rc-1/</link><pubDate>Mon, 04 Dec 2006 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2006/12/04/we-ve-just-released-groovy-rc-1/</guid><description>&lt;p>This is with great pleasure that I&amp;rsquo;m announcing the release of the first release candidate of Groovy. Groovy RC-1 is a very important milestone in the life of the project. It also means 1.0 will be released very shortly thereafter. The plan is to release the final version before the end of the month.&lt;/p>
&lt;p>This release contains a re-implemented and reworked Meta-Object Protocol, which is the core of Groovy&amp;rsquo;s runtime system which decides how the dispatch of method calls, property and attribute access works. This new MOP brings more flexibility and a finer-grained control of those mechanisms. But unless you need tweaking that advanced dispatching logic, those changes won&amp;rsquo;t affect you.&lt;/p></description><content:encoded>
<![CDATA[<p>This is with great pleasure that I&rsquo;m announcing the release of the first release candidate of Groovy. Groovy RC-1 is a very important milestone in the life of the project. It also means 1.0 will be released very shortly thereafter. The plan is to release the final version before the end of the month.</p>
<p>This release contains a re-implemented and reworked Meta-Object Protocol, which is the core of Groovy&rsquo;s runtime system which decides how the dispatch of method calls, property and attribute access works. This new MOP brings more flexibility and a finer-grained control of those mechanisms. But unless you need tweaking that advanced dispatching logic, those changes won&rsquo;t affect you.</p>
<p>Groovy RC-1 contains a lot of bug fixes and improvements &ndash; about a hundred have been worked out in this release. Among the interesting improvements, you&rsquo;ll note that coercion mechanisms are improved and now customizable for your own POGOs through the asType(Class) method. You can even coerce Maps to interfaces, as well as Closures to single-method interfaces. The &lsquo;in&rsquo; keyword now becomes a fully supported boolean operator, not only in the for loop. Last but not least in the dynamicity of the language, you can decide which method to call with GStrings, like in: foo.&quot;$methdName&quot;(*args).</p>
<p>So far, the old closure notation with the vertical pipe was still allowed, and the @Property syntax was silently ignored in the latest release. Now that the transition period is over, in RC-1 both are definitely illegal. I hope you will all have already updated your codebase.</p>
<p>For a mode detailed list of the bug fixes and improvements, please refer to <a href="http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&amp;styleName=Html&amp;version=12104">JIRA</a>. You can download Groovy RC-1 at the <a href="http://dist.groovy.codehaus.org/distributions/?C=M;O=D">usual place</a>.</p>
<p>Thanks goes to all the team for its efforts, and particularly Jochen Theodorou for his hard work. Big bravos to John Wilson for the XML support and the MOP, Guillaume Alléon for patches and Groovy SOAP, Dierk Koenig and it co-authors for the great Groovy in Action book, and the Eclipse plugin team as well. And also thanks to the brand new commiters who are bringing some fresh blood to the team, for instance Paul King who helped us improve some important error messages! The list is too long to name everybody!</p>
<p>Happy Groovying!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>From named-parameters to Domain-Specific Languages</title><link>https://glaforge.dev/posts/2006/08/13/from-named-parameters-to-domain-specific-languages/</link><pubDate>Sun, 13 Aug 2006 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2006/08/13/from-named-parameters-to-domain-specific-languages/</guid><description>&lt;p>There have always been a few irritating things to me in Java. However, working on Groovy made me go beyond these limitations, or all the useless boiler-plate code one has to write. If I had to ask for some new features in Java 7, that would certainly be:&lt;/p>
&lt;ul>
&lt;li>have closure support (or delegate or whatever you call that)&lt;/li>
&lt;li>native syntax for common data structures like lists and maps&lt;/li>
&lt;li>named parameters&lt;/li>
&lt;/ul>
&lt;p>Today, I&amp;rsquo;m going to say a few words about the last item on my list: named parameters.&lt;/p></description><content:encoded>
<![CDATA[<p>There have always been a few irritating things to me in Java. However, working on Groovy made me go beyond these limitations, or all the useless boiler-plate code one has to write. If I had to ask for some new features in Java 7, that would certainly be:</p>
<ul>
<li>have closure support (or delegate or whatever you call that)</li>
<li>native syntax for common data structures like lists and maps</li>
<li>named parameters</li>
</ul>
<p>Today, I&rsquo;m going to say a few words about the last item on my list: named parameters.</p>
<p>A few days ago, I read Patrick who was speaking about <a href="http://blogs.opensymphony.com/plightbo/2006/08/var_args_and_javas_lack_of_nam.html">var args and Java&rsquo;s lack of named parameters</a>. It also remind me about Paul&rsquo;s ideas regarding <a href="http://paulhammant.com/blog/at-least-one-unilateral-improvement-to-java.html">improvements </a>we could bring to Java. I&rsquo;m definitely on the same wave-length on those Java wishes.</p>
<p>Regarding named-parameters, despite living on the JVM (and hence its limitations), Groovy decided to provide a simple solution for passing named parameters to methods: we&rsquo;re leveraging the native syntax for Maps and the omition of square brackets. So in Groovy, when you define a map, you can use that native syntax:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> myMap <span style="color:#666">=</span> <span style="color:#666">[</span><span style="color:#002070;font-weight:bold">aKey:</span> <span style="color:#40a070">1</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">anotherKey:</span> <span style="color:#40a070">2</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">lastKey:</span> <span style="color:#40a070">3</span><span style="color:#666">]</span>
</span></span></code></pre></div><p>This is quite convenient. But we went a bit further, because when we pass a map as a method argument, we can omit the brackets, so in the end, you can make method calls look like:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>myAccount<span style="color:#666">.</span><span style="color:#4070a0">debit</span><span style="color:#666">(</span><span style="color:#002070;font-weight:bold">amount:</span> <span style="color:#40a070">500</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">currency:</span> <span style="color:#4070a0">&#34;euro&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>monster<span style="color:#666">.</span><span style="color:#4070a0">move</span><span style="color:#666">(</span><span style="color:#002070;font-weight:bold">x:</span> <span style="color:#40a070">400</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">y:</span> <span style="color:#40a070">300</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">z:</span> <span style="color:#40a070">200</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>As Groovy allows you to add methods to all classes, you&rsquo;d probably better add some more readable amounts, like with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#40a070">500</span><span style="color:#666">.</span><span style="color:#4070a0">euros</span>
</span></span><span style="display:flex;"><span><span style="color:#40a070">200</span><span style="color:#666">.</span><span style="color:#4070a0">dollars</span>
</span></span></code></pre></div><p>So the debit() method could look like that, without named parameters and parentheses:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>myAccound<span style="color:#666">.</span><span style="color:#4070a0">debit</span> <span style="color:#40a070">500</span><span style="color:#666">.</span><span style="color:#4070a0">euros</span>
</span></span></code></pre></div><p>And we&rsquo;re getting closer to <a href="http://en.wikipedia.org/wiki/Domain-specific_programming_language">Domain-Specific Languages</a>. Operator overloading would also help making the intent potentially clearer:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>myAccound <span style="color:#666">+</span> <span style="color:#40a070">500</span><span style="color:#666">.</span><span style="color:#4070a0">euros</span>
</span></span><span style="display:flex;"><span>myAccound <span style="color:#666">-</span> <span style="color:#40a070">200</span><span style="color:#666">.</span><span style="color:#4070a0">dollars</span>
</span></span><span style="display:flex;"><span>myAccount <span style="color:#666">&lt;&lt;</span> <span style="color:#40a070">500</span><span style="color:#666">.</span><span style="color:#4070a0">euros</span>
</span></span><span style="display:flex;"><span>myACcount <span style="color:#666">&gt;&gt;</span> <span style="color:#40a070">500</span><span style="color:#666">.</span><span style="color:#4070a0">euros</span>
</span></span></code></pre></div><p>I guess method calls are often clearer to read though</p>
<p>Back to DSLs, with named-parameter, closures, operator overloading, if I take again my bank account example again, we could imagine money transactions like that:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> someMoney <span style="color:#666">=</span> <span style="color:#40a070">500</span><span style="color:#666">.</span><span style="color:#4070a0">euros</span>
</span></span><span style="display:flex;"><span>InTransaction<span style="color:#666">.</span><span style="color:#4070a0">do</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    accOne<span style="color:#666">.</span><span style="color:#4070a0">credit</span><span style="color:#666">(</span> <span style="color:#002070;font-weight:bold">amount:</span> someMoney <span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    accTwo<span style="color:#666">.</span><span style="color:#4070a0">debit</span><span style="color:#666">(</span> <span style="color:#002070;font-weight:bold">amount:</span> someMoney <span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Or to symbolize the money transfer between accounts:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>InTransaction<span style="color:#666">.</span><span style="color:#4070a0">do</span> <span style="color:#666">{</span> accOne <span style="color:#666">&lt;&lt;</span> <span style="color:#40a070">500</span><span style="color:#666">.</span><span style="color:#4070a0">euros</span> <span style="color:#666">&lt;&lt;</span> accTwo <span style="color:#666">}</span>
</span></span></code></pre></div><p>Again on named-parameters, Patrick was mentioning DAO finder methods. They could advantageously be replaced with:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>dao<span style="color:#666">.</span><span style="color:#4070a0">findBy</span><span style="color:#666">(</span><span style="color:#002070;font-weight:bold">name:</span> <span style="color:#4070a0">&#34;Harry&#34;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">lastName:</span> <span style="color:#4070a0">&#34;Potter&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>dao<span style="color:#666">.</span><span style="color:#4070a0">findAllBy</span><span style="color:#666">(</span><span style="color:#002070;font-weight:bold">author:</span> <span style="color:#4070a0">&#34;Rowling&#34;</span><span style="color:#666">)</span>
</span></span></code></pre></div><p><a href="http://grails.org/">Grails</a> even goes further with finder methods by intercepting imaginary method calls that translate into queries:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> results <span style="color:#666">=</span> Book<span style="color:#666">.</span><span style="color:#4070a0">findByTitle</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;The Stand&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>results <span style="color:#666">=</span> Book<span style="color:#666">.</span><span style="color:#4070a0">findByTitleLike</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;Harry Pot%&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>results <span style="color:#666">=</span> Book<span style="color:#666">.</span><span style="color:#4070a0">findByReleaseDateBetween</span><span style="color:#666">(</span> firstDate<span style="color:#666">,</span> secondDate <span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>results <span style="color:#666">=</span> Book<span style="color:#666">.</span><span style="color:#4070a0">findByReleaseDateGreaterThan</span><span style="color:#666">(</span> someDate <span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>results <span style="color:#666">=</span> Book<span style="color:#666">.</span><span style="color:#4070a0">findByTitleLikeOrReleaseDateLessThan</span><span style="color:#666">(</span> <span style="color:#4070a0">&#34;%Something%&#34;</span><span style="color:#666">,</span> someDate <span style="color:#666">)</span>
</span></span></code></pre></div><p>Be careful to those who don&rsquo;t like long method names! But of course, there are <a href="http://grails.org/GORM#GORM-DomainClassQuerying">other solutions</a> for doing queries.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Tip: View unread mails in Gmail</title><link>https://glaforge.dev/posts/2006/07/17/tip-view-unread-mails-in-gmail/</link><pubDate>Mon, 17 Jul 2006 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2006/07/17/tip-view-unread-mails-in-gmail/</guid><description>&lt;p>When you&amp;rsquo;re subscribed to many mailing-lists, you often have hundreds of mails a day that you don&amp;rsquo;t even bother reading. So usually, you mark them as read. But if you&amp;rsquo;re like me and that your using tags as folders and sort all incoming mailing-lists in those folders, when you want to mark all mails as read, you need to go to each label and select them all, mark them as read each time. It starts to become a little boring. I&amp;rsquo;d love having a button &amp;ldquo;mark all as read&amp;rdquo; in Gmail. Unfortunately, that&amp;rsquo;s not the case. The biggest pain though, is to have to go to each label&amp;rsquo;s view to mark things as read. Fortunately, I came across a nice tip to allow me to view all unread mails altogether, without having to let everything go in my inbox (which I don&amp;rsquo;t want).&lt;/p></description><content:encoded>
<![CDATA[<p>When you&rsquo;re subscribed to many mailing-lists, you often have hundreds of mails a day that you don&rsquo;t even bother reading. So usually, you mark them as read. But if you&rsquo;re like me and that your using tags as folders and sort all incoming mailing-lists in those folders, when you want to mark all mails as read, you need to go to each label and select them all, mark them as read each time. It starts to become a little boring. I&rsquo;d love having a button &ldquo;mark all as read&rdquo; in Gmail. Unfortunately, that&rsquo;s not the case. The biggest pain though, is to have to go to each label&rsquo;s view to mark things as read. Fortunately, I came across a nice tip to allow me to view all unread mails altogether, without having to let everything go in my inbox (which I don&rsquo;t want).</p>
<p>So&hellip; the trick: in the search box, type &ldquo;<strong>label:Unread</strong>&rdquo;, and you&rsquo;ll have a view of all your unread emails, whatever label there are labelled with.</p>
<p>Now, to go further, I still wish there were a &ldquo;mark all as read&rdquo; button in each view. That would save up some time and hassle. Moreover, if one could &ldquo;save&rdquo; searches, and have them in some &ldquo;custom searches&rdquo; category, that would be awesome! And while we&rsquo;re in the wish-list-kind-of-blog-post, if Gmail could imap or pop my other email accounts, that&rsquo;d be lovely :-)</p>
<p><strong>Update #1:</strong> <a href="http://beust.com/">Cédric</a> mentionned a shorter alternative: &ldquo;is:unread&rdquo;.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Java's and Groovy's King at Versailles</title><link>https://glaforge.dev/posts/2006/07/16/java-s-and-groovy-s-king-at-versailles/</link><pubDate>Sun, 16 Jul 2006 02:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2006/07/16/java-s-and-groovy-s-king-at-versailles/</guid><description>&lt;p>&lt;figure>
&lt;a href="#img-89757e4e41c8f30176e64c6281690f99">
&lt;img src="https://glaforge.dev/img/misc/javaday-gosling-laforge.jpg"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-89757e4e41c8f30176e64c6281690f99">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/javaday-gosling-laforge.jpg"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>A few weeks ago at &lt;a href="http://fr.sun.com/javaday2006/">JavaDay 2006&lt;/a>, a nice one-day conference organized by Sun, I&amp;rsquo;ve had the pleasure to meet &lt;a href="http://blogs.sun.com/roller/page/jag">James Gosling&lt;/a>. James is the main creator of Java, and as I&amp;rsquo;m leading the &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> project, and that the conference was happening in Versailles, city of the former French kings reknown castle, it&amp;rsquo;s like two language kings were meeting there!&lt;/p>
&lt;p>The picture taken on the left is from &lt;a href="http://userfriendly.free.fr/christopher/en/">Chris&lt;/a>, my friend and former colleague. Read his &lt;a href="http://userfriendly.free.fr/christopher/en/">blog&lt;/a>, it&amp;rsquo;s full of great content! Of course, you&amp;rsquo;ll have recognized James on the left, and me on the right&amp;hellip; and not the reverse order ;-)&lt;/p></description><content:encoded>
<![CDATA[<p><figure>
  <a href="#img-89757e4e41c8f30176e64c6281690f99">
    <img src="/img/misc/javaday-gosling-laforge.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-89757e4e41c8f30176e64c6281690f99">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/javaday-gosling-laforge.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>A few weeks ago at <a href="http://fr.sun.com/javaday2006/">JavaDay 2006</a>, a nice one-day conference organized by Sun, I&rsquo;ve had the pleasure to meet <a href="http://blogs.sun.com/roller/page/jag">James Gosling</a>. James is the main creator of Java, and as I&rsquo;m leading the <a href="http://groovy.codehaus.org/">Groovy</a> project, and that the conference was happening in Versailles, city of the former French kings reknown castle, it&rsquo;s like two language kings were meeting there!</p>
<p>The picture taken on the left is from <a href="http://userfriendly.free.fr/christopher/en/">Chris</a>, my friend and former colleague. Read his <a href="http://userfriendly.free.fr/christopher/en/">blog</a>, it&rsquo;s full of great content! Of course, you&rsquo;ll have recognized James on the left, and me on the right&hellip; and not the reverse order ;-)</p>
<p>Overall, JavaDay was a nice conference, and I&rsquo;ve had the opportunity to be speaking at two panels, one with James Gosling about our feedback from JavaOne 2006, and the othere on the French vitality in the Open Source area by being a member of the <a href="http://www.ossgtp.org/">OSS-GTP</a>association gathering French commiters and contributors to key OSS projects.</p>
<p>It was also a nice occasion to meet persons I&rsquo;ve been reading in blogs like Romain Guy (Swing guru) or Ludovic Champenois (Sun), and to meet again guys like Tugdual Grall (Oracle), Emmanuel Bernard (JBoss), Alexis Moussine-Pouchkine (Sun), or Eric Mahé (Sun).</p>
<p>One of my favorite moment was when <strong>James Gosling told me that he had used Groovy on a couple of projects and that he liked it very much</strong>! What a delight and a pleasure to hear him saying that of my pet project, the project I&rsquo;ve been working on on my spare time for years now!</p>
<p><strong>Update:</strong> Someone else took a <a href="http://static.flickr.com/21/182667370_79811bd501_o.jpg">better picture of Gosling and myself</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy in Action, Manning</title><link>https://glaforge.dev/posts/2006/07/16/groovy-in-action-manning/</link><pubDate>Sun, 16 Jul 2006 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2006/07/16/groovy-in-action-manning/</guid><description>&lt;p>&lt;figure>
&lt;a href="#img-0fda773fc53074bf423ff9e2ca6b28a7">
&lt;img src="https://glaforge.dev/img/misc/gina-1.jpg"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-0fda773fc53074bf423ff9e2ca6b28a7">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/gina-1.jpg"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>Yesterday, while blogging about the latest &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> and &lt;a href="http://grails.org/">Grails&lt;/a> news, I mentioned the arrival of &lt;a href="http://www.manning.com/koenig/">Groovy in Action&lt;/a> soon to be published by Manning. And I forgot to show you the beautiful cover of the book. &amp;ldquo;Groovy in Action&amp;rdquo; is mainly written by Dierk Koenig, assisted with Andrew Glover, Paul King, Jon Skeet and myself. &lt;a href="http://blogs.sun.com/roller/page/jag">James Gosling&lt;/a> was kind to write us a foreword for the book, and when I met him at &lt;a href="http://fr.sun.com/javaday2006/">JavaDay 2006&lt;/a> two weeks ago, it was such a delight to hear him telling me that he had used Groovy in a couple of projects at Sun and that he liked it very much!&lt;/p></description><content:encoded>
<![CDATA[<p><figure>
  <a href="#img-0fda773fc53074bf423ff9e2ca6b28a7">
    <img src="/img/misc/gina-1.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-0fda773fc53074bf423ff9e2ca6b28a7">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/gina-1.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Yesterday, while blogging about the latest <a href="http://groovy.codehaus.org/">Groovy</a> and <a href="http://grails.org/">Grails</a> news, I mentioned the arrival of <a href="http://www.manning.com/koenig/">Groovy in Action</a> soon to be published by Manning. And I forgot to show you the beautiful cover of the book. &ldquo;Groovy in Action&rdquo; is mainly written by Dierk Koenig, assisted with Andrew Glover, Paul King, Jon Skeet and myself. <a href="http://blogs.sun.com/roller/page/jag">James Gosling</a> was kind to write us a foreword for the book, and when I met him at <a href="http://fr.sun.com/javaday2006/">JavaDay 2006</a> two weeks ago, it was such a delight to hear him telling me that he had used Groovy in a couple of projects at Sun and that he liked it very much!</p>
<p>For a first taste of the book, here is the abstract:</p>
<blockquote>
<p>Groovy in Action is a comprehensive description of the Groovy programming language, its libraries, and its everyday use. With the release of JSR 241, Groovy has become the second standard language for the Java platform. The book introduces Java developers to the new dynamic features that Groovy brings to this platform.</p>
<p>The first part of the book explains the basic parts of Groovy: datatypes, control flow, object model, and handling specifics. The second part elaborates on enhancements that Groovy brings to standard Java development: builders and template engines, JRE improvements (GDK), integration options, and the special support for XML, regular expressions and database programming. The hands-on experience part of the book presents various tips &amp; tricks for daily programming work, covers unit testing and build support, and shows how to even script Windows via Groovy. An additional bonus track is dedicated to Grails, the Groovy Web Application Framework.</p>
<p>Groovy in Action introduces the language by example, showing lots of reusable pieces of code while explaining the underlying concepts. Java developers who are new to Groovy will find a smooth transition into the dynamic programming world. Groovy experts will find new aspects and triggers of creativity as well as a solid reference.</p></blockquote>
<p>Despite only the first few chapters are available through the Early Access Program, if you&rsquo;re curious to know what&rsquo;s going to be in the book, Dierk Koenig provided <a href="http://www.nabble.com/RE%3A-Groovy-in-Action-now-in-MEAP-p5275813.html">table of content</a> on the Groovy mailing-lists.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy, Grails, JSR-223, books, conferences and so on...</title><link>https://glaforge.dev/posts/2006/07/15/groovy-grails-jsr-223-books-conferences-and-so-on/</link><pubDate>Sat, 15 Jul 2006 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2006/07/15/groovy-grails-jsr-223-books-conferences-and-so-on/</guid><description>&lt;p>A lot of great and interesting things are happening these days on the dynamic language front. Of course, for those who&amp;rsquo;ve been there or followed the blog reports and articles, this year&amp;rsquo;s been a pretty Groovy year so far, as I had promised last year. For instance, the &lt;a href="http://glaforge.free.fr/weblog/index.php?itemid=172">6 sessions&lt;/a> about &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> &amp;amp; &lt;a href="http://grails.org/">Grails&lt;/a> at JavaOne 2006 were well attended and packed. But that&amp;rsquo;s not all, Groovy and Grails are present at various&lt;a href="http://groovy.codehaus.org/Events">events&lt;/a>, such as the &lt;a href="http://www.ix-konferenz.de/programm.php?konferenzid=11&amp;amp;st=Programm">iX&lt;/a> conference in Germany, where Dierk Koenig and Marc Guillemot will be speaking. Another great thing that happened just a few days ago was the very first &lt;a href="http://skillsmatter.com/groovy-grails-seminar">Groovy and Grails seminar&lt;/a>where Dierk and &lt;a href="http://graemerocher.blogspot.com/">Graeme Rocher&lt;/a> talked about Groovy and Grails!&lt;/p></description><content:encoded>
<![CDATA[<p>A lot of great and interesting things are happening these days on the dynamic language front. Of course, for those who&rsquo;ve been there or followed the blog reports and articles, this year&rsquo;s been a pretty Groovy year so far, as I had promised last year. For instance, the <a href="http://glaforge.free.fr/weblog/index.php?itemid=172">6 sessions</a> about <a href="http://groovy.codehaus.org/">Groovy</a> &amp; <a href="http://grails.org/">Grails</a> at JavaOne 2006 were well attended and packed. But that&rsquo;s not all, Groovy and Grails are present at various<a href="http://groovy.codehaus.org/Events">events</a>, such as the <a href="http://www.ix-konferenz.de/programm.php?konferenzid=11&amp;st=Programm">iX</a> conference in Germany, where Dierk Koenig and Marc Guillemot will be speaking. Another great thing that happened just a few days ago was the very first <a href="http://skillsmatter.com/groovy-grails-seminar">Groovy and Grails seminar</a>where Dierk and <a href="http://graemerocher.blogspot.com/">Graeme Rocher</a> talked about Groovy and Grails!</p>
<p>Apart from this great and positive coverage we&rsquo;ve had lately, two new versions of Groovy and Grails have been released. Groovy released its <a href="http://docs.codehaus.org/display/GRAILS/2006/07/14/Grails+0.2+Released">last version before the first Release Candidate</a>, while Grails released a <a href="http://docs.codehaus.org/display/GRAILS/2006/07/14/Grails+0.2+Released">second and promising version</a>. For having played with Grails recently, I can tell you that&rsquo;s real fun to use: in a few spare cycles I had during the commuting to work, I was able to develop a full application with all its views, controllers, CRUD operations, in no time. It&rsquo;s really amazing, and when I showed that to some of my colleagues, they couldn&rsquo;t believe I only had to write about 20 lines of business code!</p>
<p>What else&hellip;</p>
<ul>
<li>Along with the new Groovy release, <a href="http://blogs.sun.com/roller/page/sundararajan/20060715">Sundar</a> upgraded the Groovy script engine for <a href="http://jcp.org/en/jsr/detail?id=223">JSR-223&rsquo;s scripting APIs</a> so that you can have the grooviest experience on Mustang.</li>
<li><a href="http://www.manning.com/koenig/">Groovy in Action</a> is now available through Manning&rsquo;s MEAP early access program! So if you can&rsquo;t wait for the paper version, please read the almost final chapters from Manning&rsquo;s site! This book is really going to become &ldquo;THE&rdquo; reference documentation for Groovy, and parts of it might be reused to improve the average quality documentation we&rsquo;ve currently have online.</li>
</ul>
<p>So, frankly, I&rsquo;m very happy these days to see Groovy and Grails progressing so nicely and quickly towards their final releases. The dynamic languages on the JVM are a nice way to move forward to a more agile and versatile enterprise environment. And it feels good to be part of this movement. The coverage we get at conferences, with blog posts, articles or even inquiries from big corporations makes me feel like we&rsquo;re on the right track!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>JavaDay 2006, Groovy spec lead, and wedding</title><link>https://glaforge.dev/posts/2006/06/26/javaday-2006-groovy-spec-lead-and-wedding/</link><pubDate>Mon, 26 Jun 2006 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2006/06/26/javaday-2006-groovy-spec-lead-and-wedding/</guid><description>&lt;p>Lots of things are happening to me these days. First of all, I just got married a week ago with the lovely woman I&amp;rsquo;ve been living with for a few years already. &lt;a href="http://eek.ook.org/">Tug&lt;/a>, my friend &amp;amp; Groovy commiter came straight from England to our wedding and took &lt;a href="http://www.flickr.com/photos/tug/sets/72157594173964767/show/">some shots of Stéphanie and me&lt;/a>. Thanks a lot to all our family and friends who were so kind to come celebrate this happy moment with us, and to all those who sent us their best wishes.&lt;/p></description><content:encoded>
<![CDATA[<p>Lots of things are happening to me these days. First of all, I just got married a week ago with the lovely woman I&rsquo;ve been living with for a few years already. <a href="http://eek.ook.org/">Tug</a>, my friend &amp; Groovy commiter came straight from England to our wedding and took <a href="http://www.flickr.com/photos/tug/sets/72157594173964767/show/">some shots of Stéphanie and me</a>. Thanks a lot to all our family and friends who were so kind to come celebrate this happy moment with us, and to all those who sent us their best wishes.</p>
<p>The second thing is that I&rsquo;ve just been nominated as the <a href="http://jcp.org/en/jsr/detail?id=241">JSR-241</a> Spec Lead. I hope under this new reign and with the upcoming release of <a href="http://groovy.codehaus.org/">Groovy 1.0</a>, Groovy will be recognized as the de facto and enterprise standard scripting language for the JVM, and perhaps even included in Java 7? Who knows&hellip; JDK 6 will bundle Derby&hellip; so why not another scripting language&hellip; :-)</p>
<p>And the last thing I wanted to mention tonight: I&rsquo;ll be speaking at the <a href="http://fr.sun.com/javaday2006/">JavaDay 2006</a> conference at Versailles, France, for a session entitled &ldquo;Xwiki, Hibernate, Groovy, &hellip; The French Java OpenSource Vitality&rdquo; where you&rsquo;ll see numerous French chaps from the <a href="http://ossgtp.org/">OSS-GTP</a> group who are active commiters to reknown OSS projects, and who will speak about their pet OSS projects. I&rsquo;ll also be part of the round table with <a href="http://blogs.sun.com/roller/page/jag">James Gosling</a> for a &ldquo;Round Table - JavaOne 2006: Ask The Experts&rdquo;. This conference is on Thursday, and I&rsquo;m looking forward to seeing you there!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>InfoQ: a community news site for the architects</title><link>https://glaforge.dev/posts/2006/05/25/infoq-a-community-news-site-for-the-architects/</link><pubDate>Thu, 25 May 2006 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2006/05/25/infoq-a-community-news-site-for-the-architects/</guid><description>&lt;p>Remember Floyd Marinescu? The founder of &lt;a href="http://www.theserverside.com/">TheServerSide&lt;/a>? He&amp;rsquo;s now working hard on a new community news site which has just been unleashed/unlaunched: &lt;a href="http://www.infoq.com/">InfoQ&lt;/a>. InfoQ is a community of communities delivering news, articles, interviews, presentations, opinions, and even mini-books on various topics targeting an audience of software and technical architects, project leads and managers. Currently, five communities (or main topics if you prefer) are available:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://java.infoq.com/">Java&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://dotnet.infoq.com/">.Net&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://ruby.infoq.com/">Ruby&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://soa.infoq.com/">SOA&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://agile.infoq.com/">Agile&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>What&amp;rsquo;s nice about this concept of communities is that you can very easily subscribe or browse only the content you&amp;rsquo;re interested in. Moreover, those communities are handled by some famous and reknown contributors, like Scott Ambler for the Agile section, or Obie Fernandez for the Ruby part. The personalization goes even further, because you can also select some sub-topics / tags specific like Architecture, Modeling, Domain-Specific Languages, etc. That&amp;rsquo;s an orthogonal clustering of information. And with all these nice aspects, everything is so&amp;hellip; Web 2.0 ;-) Full of AJAX everywhere, but for the best, not just for the shiny and trendy aspect of it.&lt;/p></description><content:encoded>
<![CDATA[<p>Remember Floyd Marinescu? The founder of <a href="http://www.theserverside.com/">TheServerSide</a>? He&rsquo;s now working hard on a new community news site which has just been unleashed/unlaunched: <a href="http://www.infoq.com/">InfoQ</a>. InfoQ is a community of communities delivering news, articles, interviews, presentations, opinions, and even mini-books on various topics targeting an audience of software and technical architects, project leads and managers. Currently, five communities (or main topics if you prefer) are available:</p>
<ul>
<li><a href="http://java.infoq.com/">Java</a></li>
<li><a href="http://dotnet.infoq.com/">.Net</a></li>
<li><a href="http://ruby.infoq.com/">Ruby</a></li>
<li><a href="http://soa.infoq.com/">SOA</a></li>
<li><a href="http://agile.infoq.com/">Agile</a></li>
</ul>
<p>What&rsquo;s nice about this concept of communities is that you can very easily subscribe or browse only the content you&rsquo;re interested in. Moreover, those communities are handled by some famous and reknown contributors, like Scott Ambler for the Agile section, or Obie Fernandez for the Ruby part. The personalization goes even further, because you can also select some sub-topics / tags specific like Architecture, Modeling, Domain-Specific Languages, etc. That&rsquo;s an orthogonal clustering of information. And with all these nice aspects, everything is so&hellip; Web 2.0 ;-) Full of AJAX everywhere, but for the best, not just for the shiny and trendy aspect of it.</p>
<p>During JavaOne 2006, I&rsquo;ve had the pleasure to have my presentation about &ldquo;Simplifying Enterprise Development with Groovy&rdquo; recorded by Floyd, so within a few months, you&rsquo;ll have the pleasure (or not) to watch me speak about Groovy! InfoQ already published a news entry about <a href="http://www.infoq.com/news/Oracle-helps-Groovy-and-Grails">Oracle&rsquo;s decision to participate in the development of Grails and Groovy</a>.</p>
<p>Good luck Floyd with this new endeavour! I&rsquo;ll be a faithful reader! As a Software Architect in my profesional life, at <a href="http://www.octo.com/">OCTO Technology</a>, a consulting company focusing on architecture, that was the kind of site I was looking for.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Builders in dynamic languages</title><link>https://glaforge.dev/posts/2006/04/18/builders-in-dynamic-languages/</link><pubDate>Tue, 18 Apr 2006 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2006/04/18/builders-in-dynamic-languages/</guid><description>&lt;p>&lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> introduced the concept of &lt;a href="http://www-128.ibm.com/developerworks/java/library/j-pg04125/">builders &lt;/a>a few years ago, and it&amp;rsquo;s great to see other dynamic languages borrow this concept. Functional languages have even already done things like that for decades! In the past, Groovy borrowed a lot of brilliant ideas to languages like Ruby or Smalltalk, and some times, that&amp;rsquo;s the reversed situation where others seem to borrow ideas from Groovy.&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://jruby.sourceforge.net/">JRuby&lt;/a> created a &lt;a href="http://glaforge.free.fr/weblog/index.php?itemid=154&amp;amp;catid=2">clone of our AntBuilder&lt;/a>&lt;/li>
&lt;li>Ruby/Rails&amp;rsquo;ve got their own &lt;a href="http://www.xml.com/lpt/a/2006/01/04/creating-xml-with-ruby-and-builder.html">Ruby builders&lt;/a>&lt;/li>
&lt;li>And I just came across an article today showing some builders in JavaScript as well with the &lt;a href="http://www.vivabit.com/bollocks/2006/04/06/introducing-dom-builder">JavaScript DOM builder&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>&lt;a href="http://grails.codehaus.org/">Grails&lt;/a> also makes heavy use of the &lt;a href="http://grails.org/Builders">builder concept&lt;/a> by letting users easily create Hibernate criteria queries, define domain classes constraints, or specify AJAX XML fragments. The builder concept is really a creative way to create &lt;a href="http://www.martinfowler.com/bliki/FluentInterface.html">fluent API&lt;/a>s and &lt;a href="http://en.wikipedia.org/wiki/Domain-specific_language">DSL&lt;/a>s. I&amp;rsquo;m sure we&amp;rsquo;ll find other great usage scenarios for it.&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://groovy.codehaus.org/">Groovy</a> introduced the concept of <a href="http://www-128.ibm.com/developerworks/java/library/j-pg04125/">builders </a>a few years ago, and it&rsquo;s great to see other dynamic languages borrow this concept. Functional languages have even already done things like that for decades! In the past, Groovy borrowed a lot of brilliant ideas to languages like Ruby or Smalltalk, and some times, that&rsquo;s the reversed situation where others seem to borrow ideas from Groovy.</p>
<ul>
<li><a href="http://jruby.sourceforge.net/">JRuby</a> created a <a href="http://glaforge.free.fr/weblog/index.php?itemid=154&amp;catid=2">clone of our AntBuilder</a></li>
<li>Ruby/Rails&rsquo;ve got their own <a href="http://www.xml.com/lpt/a/2006/01/04/creating-xml-with-ruby-and-builder.html">Ruby builders</a></li>
<li>And I just came across an article today showing some builders in JavaScript as well with the <a href="http://www.vivabit.com/bollocks/2006/04/06/introducing-dom-builder">JavaScript DOM builder</a></li>
</ul>
<p><a href="http://grails.codehaus.org/">Grails</a> also makes heavy use of the <a href="http://grails.org/Builders">builder concept</a> by letting users easily create Hibernate criteria queries, define domain classes constraints, or specify AJAX XML fragments. The builder concept is really a creative way to create <a href="http://www.martinfowler.com/bliki/FluentInterface.html">fluent API</a>s and <a href="http://en.wikipedia.org/wiki/Domain-specific_language">DSL</a>s. I&rsquo;m sure we&rsquo;ll find other great usage scenarios for it.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy interview on IndicThreads</title><link>https://glaforge.dev/posts/2006/04/06/groovy-interview-on-indicthreads/</link><pubDate>Thu, 06 Apr 2006 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2006/04/06/groovy-interview-on-indicthreads/</guid><description>&lt;p>After my &lt;a href="http://glaforge.free.fr/weblog/index.php?itemid=138">interview&lt;/a> back in august 2005, I&amp;rsquo;m happy to let you know about my &lt;a href="http://www.indicthreads.com/interviews/429/groovy_grails_scripting_enterprise_java.html">latest interview&lt;/a> I&amp;rsquo;ve just made for &lt;a href="http://www.indicthreads.com/">IndicThreads&lt;/a>. In this interview, I&amp;rsquo;m speaking of course about &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> and &lt;a href="http://grails.codehaus.org/">Grails&lt;/a>, our innovative and advanced web framework lead by &lt;a href="http://graemerocher.blogspot.com/">Graeme Rocher&lt;/a>.&lt;/p></description><content:encoded>
<![CDATA[<p>After my <a href="http://glaforge.free.fr/weblog/index.php?itemid=138">interview</a> back in august 2005, I&rsquo;m happy to let you know about my <a href="http://www.indicthreads.com/interviews/429/groovy_grails_scripting_enterprise_java.html">latest interview</a> I&rsquo;ve just made for <a href="http://www.indicthreads.com/">IndicThreads</a>. In this interview, I&rsquo;m speaking of course about <a href="http://groovy.codehaus.org/">Groovy</a> and <a href="http://grails.codehaus.org/">Grails</a>, our innovative and advanced web framework lead by <a href="http://graemerocher.blogspot.com/">Graeme Rocher</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Scripting at JavaOne 2006</title><link>https://glaforge.dev/posts/2006/03/20/scripting-at-javaone-2006/</link><pubDate>Mon, 20 Mar 2006 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2006/03/20/scripting-at-javaone-2006/</guid><description>&lt;p>Scripting is definitely in fashion these days on the Java Virtual Machine. The &lt;a href="http://java.sun.com/javaone/sf/">JavaOne 2006&lt;/a> &lt;a href="http://www.cplan.com/javaone2006/contentcatalog">session catalog&lt;/a> is online, and by browsing it, you&amp;rsquo;ll notice there are several sessions and BOFs dedicated to scripting. By simply searching for the word &amp;ldquo;scripting&amp;rdquo; in the content catalog viewer, you&amp;rsquo;ll count no less than 13 sessions speaking about scripting.&lt;/p>
&lt;p>This will be my first time at JavaOne. And a big first time since I&amp;rsquo;ll be presenting a session! Along with &lt;a href="http://www.grallandco.com/blog/index.html">Tugdual Grall&lt;/a> from Oracle, we&amp;rsquo;ll be presenting &lt;strong>TS-1246&lt;/strong> entitled &lt;strong>&amp;ldquo;Simplify Enterprise Development With Scripting&amp;rdquo;&lt;/strong> in the &lt;em>&amp;ldquo;Core Platform, JAVA SE&amp;rdquo;&lt;/em> track. The abstract of the session is:&lt;/p></description><content:encoded>
<![CDATA[<p>Scripting is definitely in fashion these days on the Java Virtual Machine. The <a href="http://java.sun.com/javaone/sf/">JavaOne 2006</a> <a href="http://www.cplan.com/javaone2006/contentcatalog">session catalog</a> is online, and by browsing it, you&rsquo;ll notice there are several sessions and BOFs dedicated to scripting. By simply searching for the word &ldquo;scripting&rdquo; in the content catalog viewer, you&rsquo;ll count no less than 13 sessions speaking about scripting.</p>
<p>This will be my first time at JavaOne. And a big first time since I&rsquo;ll be presenting a session! Along with <a href="http://www.grallandco.com/blog/index.html">Tugdual Grall</a> from Oracle, we&rsquo;ll be presenting <strong>TS-1246</strong> entitled <strong>&ldquo;Simplify Enterprise Development With Scripting&rdquo;</strong> in the <em>&ldquo;Core Platform, JAVA SE&rdquo;</em> track. The abstract of the session is:</p>
<blockquote>
<p>Scripting languages (a.k.a. dynamic languages) provide a different style of programming than system languages such as the Java programming language. They enable developers to rapidly glue components together to build applications. The Java platform now has multiple scripting solutions - Groovy, JRuby, Jython, and Rhino - that offer the power of Java technology by using the simplicity of typeless, interpreted scripting languages. This session demonstrates how scripting can simplify your everyday life by helping you administer clusters of servers or test or mock up your web services in no time. Through concrete demonstrations, the presentation explains how the dynamic nature of those languages can bring more agility to your service-oriented architecture and your fashionable AJAX applications. Finally, it discusses the future of scripting to give you a taste of tools you&rsquo;ll be using tomorrow.</p></blockquote>
<p>So if you&rsquo;re coming to JavaOne this year, be sure to attend our session, and come and say hi!</p>
<p><a href="http://groovy.codehaus.org/">Groovy</a> and <a href="http://grails.codehaus.org/">Grails</a> will be fairly well covered this year. Grails has got its own <a href="https://www28.cplan.com/javaone06_cv_124_1/sessions_catalog.jsp?ilc=124-1&amp;ilg=english&amp;isort=1&amp;is=%3CISEARCH%3E&amp;ip=yes&amp;itrack=+&amp;isession_type=+&amp;isession_id=&amp;iabstract=grails&amp;ispeaker=&amp;ispk_company=">BOF session</a> (<strong>BOF-2521</strong> about <em>Grails</em>, and <strong>TS-1664</strong> about <em>&ldquo;RAD for the Java Platform Web Tier: Frameworks Smackdown&rdquo;</em>) presented by <a href="http://graemerocher.blogspot.com/">Graeme Rocher</a>, the Grails project lead.</p>
<p><a href="http://groovy.codehaus.org/">Groovy</a> is mentionned in <strong>6 sessions</strong>!</p>
<ul>
<li>There&rsquo;s Tugdual and I&rsquo;s talk on &ldquo;Simplifying Enterprise Development With Scripting&rdquo; (TS-1246),</li>
<li>Graeme&rsquo;s BOF on Grails (BOF-2521),</li>
<li>as well as Rod Cope yearly session on Groovy: &ldquo;Groovy = Java Technology + Ruby + Python for the JVM&rdquo; (TS-3273).</li>
</ul>
<p>There should be a very interesting BOF on &ldquo;Dynamic Scripting Languages&rdquo; (BOF-0554). And to finish, there are two very interesting use cases of Groovy integration:</p>
<ul>
<li>&ldquo;Flash-Gridding with Java Technology: Using Glassfish, Jini/JavaSpaces, and Groovy as an Environment for an Open Source, Self-Assembling Supercomputer&rdquo; (TS-3714)</li>
<li>&ldquo;Groovy Goes RFID with Smart Sensors for Real-World Control&rdquo; (TS-5386)</li>
</ul>
<p>I was also pretty curious to see whether other scripting languages were represented, and I only noticed one session dedicated to JRuby: &ldquo;JRuby: Bringing Ruby to the JVM Software&rdquo; (TS-3059). Jython is mentionned in some talks (including the ones on Groovy), with only one session including a demo using Jython. BeanShell is mentionned in the session on TestNG. Rhino is mentioned in my talk only. But JavaScript is very present in that on the client-side, for web applications, there&rsquo;s always been a lot of presentations speaking about it. So overall, it seems Groovy took the lion-share of the scripting languages for the JVM and we&rsquo;ve got almost twice the coverage of last year. I&rsquo;m very pleased to see the progress Groovy made both in terms of the quality of the project, and the mindshare we&rsquo;ve garnered so far.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Google Summer of Code 2005 TShirt</title><link>https://glaforge.dev/posts/2005/12/27/google-summer-of-code-2005-tshirt/</link><pubDate>Tue, 27 Dec 2005 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2005/12/27/google-summer-of-code-2005-tshirt/</guid><description>&lt;p>As a gift for mentoring students for the &lt;a href="http://code.google.com/summerofcode.html">Google summer of code 2005&lt;/a> around some project ideas for &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a>, I just received a nice tshirt roughly on time for Christmas!&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-ec9d2bef79f7e2ccbd64293252c0a370">
&lt;img src="https://glaforge.dev/img/misc/google-soc-tshirt.jpg"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-ec9d2bef79f7e2ccbd64293252c0a370">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/google-soc-tshirt.jpg"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p></description><content:encoded>
<![CDATA[<p>As a gift for mentoring students for the <a href="http://code.google.com/summerofcode.html">Google summer of code 2005</a> around some project ideas for <a href="http://groovy.codehaus.org/">Groovy</a>, I just received a nice tshirt roughly on time for Christmas!</p>
<p><figure>
  <a href="#img-ec9d2bef79f7e2ccbd64293252c0a370">
    <img src="/img/misc/google-soc-tshirt.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-ec9d2bef79f7e2ccbd64293252c0a370">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/google-soc-tshirt.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Debugging XML parser issues</title><link>https://glaforge.dev/posts/2005/12/04/debugging-xml-parser-issues/</link><pubDate>Sun, 04 Dec 2005 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2005/12/04/debugging-xml-parser-issues/</guid><description>&lt;p>There are often some issues that can waste several hours of your precious time: XML parser incompatibilities depending on your platform and application, i18n problems where several elements of your architecture aren&amp;rsquo;t configured well to serve correct encoded and localized content, class loader hierarchy nightmares, or even Jar Hell when different libraries you depend on require different versions of the same jar. Today, I&amp;rsquo;m going to concentrate on the first problem: XML parser issues.&lt;/p></description><content:encoded>
<![CDATA[<p>There are often some issues that can waste several hours of your precious time: XML parser incompatibilities depending on your platform and application, i18n problems where several elements of your architecture aren&rsquo;t configured well to serve correct encoded and localized content, class loader hierarchy nightmares, or even Jar Hell when different libraries you depend on require different versions of the same jar. Today, I&rsquo;m going to concentrate on the first problem: XML parser issues.</p>
<p>I was having a problem of interoperability between my Java web services and a .Net client consumming them. And it appeared that a bug in certain versions of Xalan made the XML payloads generated by .Net not parseable and generated some odd stacktrace on my backend with a pretty obscure message (NAMESPACE_ERR message). It took me quite some time to discover what the problem really was. In short, my old version of Xalan couldn&rsquo;t grok default namespaces inherited from parent nodes.</p>
<p>While debugging this problem, I created a little JSP page, to help me understand clearly which version of Xerces and Xalan I was using. I had no problem under Tomcat 5.0.x and Sun&rsquo;s JDK 1.4.2_06, but the problem appeared with Websphere 5.1 and IBM&rsquo;s JDK 1.4.2. My JSP page will tell you which versions of Xalan and Xerces you&rsquo;re using, from which Jars they are loaded, and it&rsquo;ll also print your system properties.</p>
<p>Just so that I can cut&rsquo;n paste it some day in the future if I have a problem with XML parsers again, here it is:</p>
<pre tabindex="0"><code class="language-jsp" data-lang="jsp">&lt;%@ page import=&#34;java.util.Properties&#34;%&gt;
&lt;%@ page import=&#34;java.util.Enumeration&#34;%&gt;
&lt;%@ page import=&#34;javax.xml.transform.TransformerFactory&#34;%&gt;
&lt;%@ page import=&#34;javax.xml.parsers.DocumentBuilderFactory&#34;%&gt;
&lt;%@ page import=&#34;javax.xml.parsers.ParserConfigurationException&#34;%&gt;
&lt;%@ page import=&#34;javax.xml.parsers.SAXParserFactory&#34;%&gt;
&lt;%@ page import=&#34;javax.xml.parsers.FactoryConfigurationError&#34;%&gt;
&lt;%@ page import=&#34;javax.xml.transform.TransformerFactoryConfigurationError&#34;%&gt;
&lt;%@ page import=&#34;java.net.URL&#34;%&gt;
&lt;%@ page import=&#34;org.apache.xalan.xslt.EnvironmentCheck&#34;%&gt;
&lt;%@ page import=&#34;java.io.PrintWriter&#34;%&gt;
&lt;%@ page contentType=&#34;text/html;charset=UTF-8&#34; language=&#34;java&#34; %&gt;
&lt;%!
    private String getJarForClass(Class clazz) {
        String clsResPath = clazz.getName().replaceAll(&#34;\\.&#34;,&#34;/&#34;)+&#34;.class&#34;;

        ClassLoader clsLoader = clazz.getClassLoader();
        if (clsLoader == null) clsLoader = ClassLoader.getSystemClassLoader();

        URL clsResURL = clsLoader.getResource(clsResPath);
        return clsResURL != null ? clsResURL.toExternalForm() : &#34;unknown&#34;;
    }
%&gt;
&lt;%
    String transformerClassName = &#34;&#34;;
    try {
        Class clazz = TransformerFactory.newInstance().getClass();
        transformerClassName = clazz.getName() + &#34;
(&#34; + getJarForClass(clazz) + &#34;)&#34;;
    } catch (TransformerFactoryConfigurationError e) {
        transformerClassName = e.getMessage();
    }

    String documentBuilderClassName = &#34;&#34;;
    try {
        Class clazz = DocumentBuilderFactory.newInstance().newDocumentBuilder().getClass();
        documentBuilderClassName = clazz.getName() + &#34;
(&#34; + getJarForClass(clazz) + &#34;)&#34;;
    } catch (ParserConfigurationException e) {
        documentBuilderClassName = e.getMessage();
    }

    String saxParserFactoryClassName = &#34;&#34;;
    try {
        Class clazz = SAXParserFactory.newInstance().getClass();
        saxParserFactoryClassName = clazz.getName() + &#34;
(&#34; + getJarForClass(clazz) + &#34;)&#34;;
    } catch (FactoryConfigurationError e) {
        saxParserFactoryClassName = e.getMessage();
    }

%&gt;

    XML parsers debugging
    
    Debug parameters
&lt;%
    Properties p = System.getProperties();
    Enumeration keys = p.keys();
    while( keys.hasMoreElements() ) {
        String key = (String) keys.nextElement(); %&gt;
        &lt;%
    }
%&gt;
    
   
        
        
    
     
            
      Xalan version
            
      &lt;%= org.apache.xalan.Version.getVersion() %&gt;
        
     
        
     
            
      TransformerFactory
            
      &lt;%= transformerClassName %&gt;
        
     
        
     
            
      DocumentBuilder
            
      &lt;%= documentBuilderClassName %&gt;
        
     
        
     
            
      SAXParserFactory
            
      &lt;%= saxParserFactoryClassName %&gt;
        
     
        
     
            
      Xalan debug environement check:
            
      &lt;% (new EnvironmentCheck()).checkEnvironment(new PrintWriter(out)); %&gt;
        
     
     
            
      &lt;%= key %&gt;
            
      &lt;%= System.getProperty(key) %&gt;
        
     
    
   
    
</code></pre><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>AntBuilder: Imitation is the Best Form of Flattery</title><link>https://glaforge.dev/posts/2005/12/03/antbuilder-imitation-is-the-best-form-of-flattery/</link><pubDate>Sat, 03 Dec 2005 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2005/12/03/antbuilder-imitation-is-the-best-form-of-flattery/</guid><description>&lt;p>I&amp;rsquo;ve just come across a &lt;a href="http://antbuilder.rubyforge.org/">clone&lt;/a> of &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a>&amp;rsquo;s AntBuilder. It&amp;rsquo;s being built with &lt;a href="http://jruby.sourceforge.net/">JRuby&lt;/a> and allows you to write your build scripts in JRuby by reusing Ant&amp;rsquo;s tasks.&lt;/p>
&lt;p>That was a pleasure to see Ruby inspired by our concept of builders, and now, they also copy our own builders. I&amp;rsquo;d simply say that imitation is the best form of flattery. Kudos guys!&lt;/p></description><content:encoded>
<![CDATA[<p>I&rsquo;ve just come across a <a href="http://antbuilder.rubyforge.org/">clone</a> of <a href="http://groovy.codehaus.org/">Groovy</a>&rsquo;s AntBuilder. It&rsquo;s being built with <a href="http://jruby.sourceforge.net/">JRuby</a> and allows you to write your build scripts in JRuby by reusing Ant&rsquo;s tasks.</p>
<p>That was a pleasure to see Ruby inspired by our concept of builders, and now, they also copy our own builders. I&rsquo;d simply say that imitation is the best form of flattery. Kudos guys!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Big thanks for the second Groovy meeting</title><link>https://glaforge.dev/posts/2005/12/01/big-thanks-for-the-second-groovy-meeting/</link><pubDate>Thu, 01 Dec 2005 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2005/12/01/big-thanks-for-the-second-groovy-meeting/</guid><description>&lt;p>Last week took place the second GroovyOne meeting in Paris, gathering the main Groovy developers. The event went fairly well, despite some disagreement we need to iron out, we made quite a lot of&lt;a href="http://docs.codehaus.org/display/GroovyJSR/Paris+Groovy+Meeting+report">progress&lt;/a> on the Groovy front towards 1.0.&lt;/p>
&lt;p>It&amp;rsquo;s been a real pleasure to meet together again, and I must say I really &lt;a href="http://www.flickr.com/photos/tags/jsr241">had a lot of fun&lt;/a> there. But this meeting could not have been possible without the help and sponsoring of different actors. That&amp;rsquo;s why I particularly want to thank all those who helped me make this happen.&lt;/p></description><content:encoded>
<![CDATA[<p>Last week took place the second GroovyOne meeting in Paris, gathering the main Groovy developers. The event went fairly well, despite some disagreement we need to iron out, we made quite a lot of<a href="http://docs.codehaus.org/display/GroovyJSR/Paris+Groovy+Meeting+report">progress</a> on the Groovy front towards 1.0.</p>
<p>It&rsquo;s been a real pleasure to meet together again, and I must say I really <a href="http://www.flickr.com/photos/tags/jsr241">had a lot of fun</a> there. But this meeting could not have been possible without the help and sponsoring of different actors. That&rsquo;s why I particularly want to thank all those who helped me make this happen.</p>
<p>First of all, <a href="http://www.codehaus.org/">Codehaus</a>&rsquo; <a href="http://www.openxource.com/">Bob McWhirter</a> alloted a budget to help us pay for the accomodations. And we were also generously sponsored by two Groovy fans building software which embed Groovy: <a href="http://www.ludovic.org/">Ludovic Dubost</a> who&rsquo;s working on <a href="http://www.xwiki.org/">XWiki</a>, an Open Source second generation Wiki engine and application platform, and <a href="http://www.exoplatform.com/">Benjamin Mestrallet</a> who&rsquo;s been building <a href="http://www.exoplatform.com/">eXo Platform</a>, powerful Open Source corporate portal and content management system. And the final thanks go to <a href="http://blogs.sun.com/roller/page/alexismp">Alexis Moussine-Pouchkine</a>, a Sun architect and consultant, who kindly granted us access to a fully-equiped meeting room in Sun&rsquo;s parisian Customer Briefing Center.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Four years to fix a trivial bug...</title><link>https://glaforge.dev/posts/2005/11/26/four-years-to-fix-a-trivial-bug/</link><pubDate>Sat, 26 Nov 2005 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2005/11/26/four-years-to-fix-a-trivial-bug/</guid><description>&lt;p>My friend Christopher just told me a &lt;a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058">very old bug in the JDK&lt;/a> has eventually been fixed in Mustang!&lt;/p>
&lt;p>&lt;strong>UTF-8 encoding does not recognize initial BOM&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>Java does not recognize the optional BOM which can begin a UTF-8 stream. It treats the BOM as if it were the initial character of the stream.A Utf-8 stream can optionally beign with a byte order mark (see, for example &lt;a href="http://www.unicode.org.unicode/faq/utf_bom.html">http://www.unicode.org.unicode/faq/utf_bom.html&lt;/a>). This is the character FEFF, which is represented as EF BB BF in utf-8. Java&amp;rsquo;s utf-8 encoding does not recognize this character as a BOM, though; the result of reading such a stream is a set of characters beginning with FEFF.&lt;/p></description><content:encoded>
<![CDATA[<p>My friend Christopher just told me a <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058">very old bug in the JDK</a> has eventually been fixed in Mustang!</p>
<p><strong>UTF-8 encoding does not recognize initial BOM</strong></p>
<blockquote>
<p>Java does not recognize the optional BOM which can begin a UTF-8 stream. It treats the BOM as if it were the initial character of the stream.A Utf-8 stream can optionally beign with a byte order mark (see, for example <a href="http://www.unicode.org.unicode/faq/utf_bom.html">http://www.unicode.org.unicode/faq/utf_bom.html</a>). This is the character FEFF, which is represented as EF BB BF in utf-8. Java&rsquo;s utf-8 encoding does not recognize this character as a BOM, though; the result of reading such a stream is a set of characters beginning with FEFF.</p></blockquote>
<p>I&rsquo;m glad it&rsquo;s now fixed! I&rsquo;ve been working around this bug for a long time already. I&rsquo;ve even applied some patches to work around this issue to <a href="http://groovy.codehaus.org/">Groovy</a> as well as in <a href="http://www.jetbrains.com/idea">IntelliJ IDEA</a> &ndash; thanks to Maxim who introduced my suggested fix inside this wonderful IDE.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Re: Ted Neward on anonymous generic methods</title><link>https://glaforge.dev/posts/2005/11/09/re-ted-neward-on-anonymous-generic-methods/</link><pubDate>Wed, 09 Nov 2005 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2005/11/09/re-ted-neward-on-anonymous-generic-methods/</guid><description>&lt;p>C#&amp;rsquo;s fanatic Ted Neward is amazed at how cool C#&amp;rsquo;s &lt;a href="http://blogs.tedneward.com/CommentView,guid,f96809ba-7332-4df7-84c8-490ec66c584f.aspx">anonymous generic methods&lt;/a> are. He then gives an example of a little program that filters some entries according to a critiria, using some generic types and delegates. It&amp;rsquo;s about finding all persons from a set of persons whose last name is &amp;ldquo;Neward&amp;rdquo;. Sam Pullara even showed his own version of the &lt;a href="http://www.javarants.com/B1823453972/C1464297901/E20051108213800/index.html">same program in Java&lt;/a> and explains that &lt;a href="http://www.jetbrains.com/">IntelliJ IDEA&lt;/a>allowed him to type roughly 10% of the characters of the program thanks to the wonderful completion and code templating capabilities of that IDE.&lt;/p></description><content:encoded>
<![CDATA[<p>C#&rsquo;s fanatic Ted Neward is amazed at how cool C#&rsquo;s <a href="http://blogs.tedneward.com/CommentView,guid,f96809ba-7332-4df7-84c8-490ec66c584f.aspx">anonymous generic methods</a> are. He then gives an example of a little program that filters some entries according to a critiria, using some generic types and delegates. It&rsquo;s about finding all persons from a set of persons whose last name is &ldquo;Neward&rdquo;. Sam Pullara even showed his own version of the <a href="http://www.javarants.com/B1823453972/C1464297901/E20051108213800/index.html">same program in Java</a> and explains that <a href="http://www.jetbrains.com/">IntelliJ IDEA</a>allowed him to type roughly 10% of the characters of the program thanks to the wonderful completion and code templating capabilities of that IDE.</p>
<p>What strikes me is how verbose both versions are. Even C# with its delegate isn&rsquo;t particularly less verbose, except a few lines saved. No real readability, expressiveness or productivity gained &ndash; though I must confess I do like all those nifty little features available in the upcoming C# 3.0. Naturally, I was tempted to give my own <a href="http://groovy.codehaus.org/">Groovy</a> version of the problem. Here it is, and please note the conciseness and clarity of the following code:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">Person</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Property</span> String firstName
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Property</span> String lastName
</span></span><span style="display:flex;"><span>    <span style="color:#555;font-weight:bold">@Property</span> <span style="color:#902000">int</span> age
</span></span><span style="display:flex;"><span>    String <span style="color:#06287e">toString</span><span style="color:#666">()</span> <span style="color:#666">{</span> <span style="color:#4070a0">&#34;Person{firstName=&#39;$firstName&#39;, lastName=&#39;$lastName&#39;, age=$age}&#34;</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> persons <span style="color:#666">=</span> <span style="color:#666">[</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">Person</span><span style="color:#666">(</span><span style="color:#002070;font-weight:bold">firstName:</span> <span style="color:#4070a0">&#34;Cathi&#34;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">lastName:</span> <span style="color:#4070a0">&#34;Gero&#34;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">age:</span> <span style="color:#40a070">35</span><span style="color:#666">),</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">Person</span><span style="color:#666">(</span><span style="color:#002070;font-weight:bold">firstName:</span> <span style="color:#4070a0">&#34;Ted&#34;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">lastName:</span> <span style="color:#4070a0">&#34;Neward&#34;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">age:</span> <span style="color:#40a070">35</span><span style="color:#666">),</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">Person</span><span style="color:#666">(</span><span style="color:#002070;font-weight:bold">firstName:</span> <span style="color:#4070a0">&#34;Stephanie&#34;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">lastName:</span> <span style="color:#4070a0">&#34;Gero&#34;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">age:</span> <span style="color:#40a070">12</span><span style="color:#666">),</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">Person</span><span style="color:#666">(</span><span style="color:#002070;font-weight:bold">firstName:</span> <span style="color:#4070a0">&#34;Michael&#34;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">lastName:</span> <span style="color:#4070a0">&#34;Neward&#34;</span><span style="color:#666">,</span> <span style="color:#002070;font-weight:bold">age:</span> <span style="color:#40a070">12</span><span style="color:#666">),</span>
</span></span><span style="display:flex;"><span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> newards <span style="color:#666">=</span> persons<span style="color:#666">.</span><span style="color:#4070a0">findAll</span><span style="color:#666">{</span> it<span style="color:#666">.</span><span style="color:#4070a0">lastName</span> <span style="color:#666">==</span> <span style="color:#4070a0">&#34;Neward&#34;</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>newards<span style="color:#666">.</span><span style="color:#4070a0">each</span><span style="color:#666">{</span> println it <span style="color:#666">}</span>
</span></span></code></pre></div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Google Base, Ning, or how to store your life</title><link>https://glaforge.dev/posts/2005/10/26/google-base-ning-or-how-to-store-your-life/</link><pubDate>Wed, 26 Oct 2005 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2005/10/26/google-base-ning-or-how-to-store-your-life/</guid><description>&lt;p>We&amp;rsquo;ve got &lt;a href="http://en.wikipedia.org/wiki/Personal_information_manager">PIM applications&lt;/a>, all sorts of &lt;a href="http://www.palm.com/">PDAs&lt;/a>, rich client GUIs or webapps on the internet or behind the corporate intranet&amp;rsquo;s firewalls. There&amp;rsquo;s the &lt;a href="http://www.andromda.org/">MDA&lt;/a> approach to generate apps from datamodels, or&lt;a href="http://www.reflexe.fr/">application generation engines&lt;/a> that build applications dynamically thanks to the interpretation of a metamodel. All these applications and interfaces to store all kind of data, personal or business related, generally sports a fixed and frozen structure. New developments are always needed for evolving your applications, and costly redeployments and interuption of services are often triggered.&lt;/p></description><content:encoded>
<![CDATA[<p>We&rsquo;ve got <a href="http://en.wikipedia.org/wiki/Personal_information_manager">PIM applications</a>, all sorts of <a href="http://www.palm.com/">PDAs</a>, rich client GUIs or webapps on the internet or behind the corporate intranet&rsquo;s firewalls. There&rsquo;s the <a href="http://www.andromda.org/">MDA</a> approach to generate apps from datamodels, or<a href="http://www.reflexe.fr/">application generation engines</a> that build applications dynamically thanks to the interpretation of a metamodel. All these applications and interfaces to store all kind of data, personal or business related, generally sports a fixed and frozen structure. New developments are always needed for evolving your applications, and costly redeployments and interuption of services are often triggered.</p>
<p>Then, there&rsquo;s the <a href="http://www.w3.org/2001/sw/">Semantic Web</a>, and <a href="http://simile.mit.edu/reports/stores/">RDF stores</a>, like <a href="http://jena.sourceforge.net/">Jena</a>/<a href="http://www.joseki.org/">Joseki</a> or <a href="http://www.kowari.org/">Kowari</a>. You can store all the information you want in some unstructured way (<a href="http://www.w3.org/RDF/">RDF triples</a>), that can be understood by other applications, if you share common ontologies through <a href="http://www.w3.org/TR/rdf-schema/">RDFS</a> or <a href="http://www.w3.org/TR/owl-features/">OWL</a>, and common namespaces like <a href="http://dublincore.org/">Dublin Core</a>. Some interesting querying systems like <a href="http://www.w3.org/TR/rdf-sparql-query/">SPARQL</a> can allow you to query multipe documents and stores at the same to help you mix together different channels of information. Unfortunately, so far, the Semantic Web never really took off because of its inherent complexity to deploy and understand.</p>
<p>These days, there&rsquo;s a couple of other concepts that&rsquo;ve taken the blogosphere by storm: <a href="http://rest.blueoxen.net/cgi-bin/wiki.pl">REST</a> and tags. <a href="http://www.flickr.com/">Flickr</a> lets you store pictures, share and tag them, as well as <a href="http://del.icio.us/">del.icio.us</a> which allows you to tag and share your bookmarks, <a href="http://maps.google.com/">Google maps</a> to share geographic data. There are also some nice REST interfaces to mix and match those online services through their public APIs. <a href="http://www.programmableweb.com/matrix">Mashups listings</a> are appearing to explain who&rsquo;s glueing with whom. And you can also subscribe to everything through <a href="http://blogs.law.harvard.edu/tech/rss">RSS</a> or <a href="http://www.intertwingly.net/wiki/pie/FrontPage">Atom</a> feeds.</p>
<p>A few weeks ago, <a href="http://en.wikipedia.org/wiki/Marc_Andreesen">Marc Andreessen</a> (<a href="http://en.wikipedia.org/wiki/Netscape_Communications_Corporation">Netscape</a>&rsquo;s founder himself) introduced <a href="http://www.ning.com/">Ning</a> to the world! It builds on the concepts of tags, REST and a custom content store, to allow its users to build <a href="http://www.emilychang.com/go/ehub/">social software</a>on top of their framework. Building your own application is as simple as creating a few types to store instances in their <a href="http://developerdocumentation.ning.com/post.php?Post:slug=ContentStore">content store</a> (no tedious SQL code anymore for your data model!), and writing a few<a href="http://www.php.net/">PHP</a> files that let users interact with their data. Or you can even clone an existing application and customize it to suit your needs! Anything is shareable (information is either public or private) and taggable. All applications and data are available through some <a href="http://developerdocumentation.ning.com/post.php?Post:slug=Pivot">pivot URLs</a> which are a kind of automatic RESTified interface of the whole system. Brilliant, really brilliant.</p>
<p>And now comes <a href="http://www.google.com/">Google</a>, and its <a href="http://base.google.com/">Google Base</a>. <a href="http://dannyayers.com/archives/2005/10/25/google-base/">Not</a> <a href="http://www.seweso.com/blog/2005/10/google-base.php">much</a> <a href="http://blog.outer-court.com/forum/11831.html">is</a> <a href="http://blog.outer-court.com/forum/11876.html">known</a> <a href="http://battellemedia.com/archives/001960.php">as</a> of yet, but I&rsquo;m sure we&rsquo;ll get more tidbits real soon. Google Base will probably be the next big thing. I was sure Google would create such a thing because they claim their goal is to organize the world&rsquo;s data. And your own personal data as well. So it seemed like a logical step to create another killer application which would allow you to store an d share all kind of structured and unstructured information. They&rsquo;ve already got the backend ready for that with their <a href="http://labs.google.com/papers/gfs.html">GFS</a> (distributed Google File System) and their <a href="http://andrewhitchcock.org/?post=214">BigTable</a> (a huge flexible and evolutive<a href="http://torrez.us/archives/2005/10/24/407">database</a> ) system. Imagine you can define your own types, in a very flexible and malleable way, that you can create list and item views of that data, that you can share these things with your friends, and tag them? And imagine also that there are APIs on top of Google Base to interact remotely with your data and make your own data-driven application interact with each other? That&rsquo;s a Semantic Web driven by data and by <a href="http://en.wikipedia.org/wiki/Folksonomy">folksonomies</a>.</p>
<p>I&rsquo;ve always dreamt of building something like that, and it seems both Google Base and Ning are going in the right direction. It seems even more useless now for me to start a project like that, since those bright guys are doing it! I&rsquo;ve always wanted to store my life, to brain dump my ideas into a common base, programmable through APIs, cross- linkable and categorizable with tags. It seems like my ideas for the future of information technology are taking shape at Google labs&hellip; today. I&rsquo;m impatient to try that out.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>JBoss' Wiki portlet, why not XWiki?</title><link>https://glaforge.dev/posts/2005/09/10/jboss-wiki-portlet-why-not-xwiki/</link><pubDate>Sat, 10 Sep 2005 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2005/09/10/jboss-wiki-portlet-why-not-xwiki/</guid><description>&lt;p>Sometimes, there are some unlogical choices that are being made: &lt;a href="http://www.theserverside.com/news/thread.tss?thread_id=36452">JBoss chooses&lt;/a> &lt;a href="http://www.jspwiki.org/">JSPWiki&lt;/a> as a base for their Wiki portlet in JBoss Portal. They choose to fork and trim JSPWiki (rather than contributing to it) to be able to embed a Wiki engine in their portal as a portlet. Fine. But why not choosing &lt;a href="http://www.xwiki.org/">XWiki&lt;/a>? XWiki:&lt;/p>
&lt;ul>
&lt;li>is a member of the &lt;a href="http://www.jboss.org/federation/MemberXWiki">JBoss Open Source Federation&lt;/a>,&lt;/li>
&lt;li>can already be &lt;a href="http://www.jboss.org/federation/MemberXWiki">integrated in JBoss Portal&lt;/a>,&lt;/li>
&lt;li>and moreover can also be integrated in any portlet container (The proof is that the &lt;a href="http://www.exoplatform.com/">eXo Platform&lt;/a> already embeds XWiki as a portlet).&lt;/li>
&lt;/ul>
&lt;p>That&amp;rsquo;s weird, isn&amp;rsquo;t it?&lt;/p></description><content:encoded>
<![CDATA[<p>Sometimes, there are some unlogical choices that are being made: <a href="http://www.theserverside.com/news/thread.tss?thread_id=36452">JBoss chooses</a> <a href="http://www.jspwiki.org/">JSPWiki</a> as a base for their Wiki portlet in JBoss Portal. They choose to fork and trim JSPWiki (rather than contributing to it) to be able to embed a Wiki engine in their portal as a portlet. Fine. But why not choosing <a href="http://www.xwiki.org/">XWiki</a>? XWiki:</p>
<ul>
<li>is a member of the <a href="http://www.jboss.org/federation/MemberXWiki">JBoss Open Source Federation</a>,</li>
<li>can already be <a href="http://www.jboss.org/federation/MemberXWiki">integrated in JBoss Portal</a>,</li>
<li>and moreover can also be integrated in any portlet container (The proof is that the <a href="http://www.exoplatform.com/">eXo Platform</a> already embeds XWiki as a portlet).</li>
</ul>
<p>That&rsquo;s weird, isn&rsquo;t it?</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Web services RPC calls over Google Talk</title><link>https://glaforge.dev/posts/2005/09/08/web-services-rpc-calls-over-google-talk/</link><pubDate>Thu, 08 Sep 2005 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2005/09/08/web-services-rpc-calls-over-google-talk/</guid><description>&lt;p>With the recent release of &lt;a href="http://www.google.com/talk/">Google Talk&lt;/a>, the fine chaps at Google entered the Instant Messenging market. The most clever step in that direction was their choice of protocol for their IM solution: &lt;a href="http://www.xmpp.org/">XMPP&lt;/a>. XMPP was popularized and standardized through the IETF by the &lt;a href="http://www.jabber.org/">Jabber&lt;/a> software foundation with its famous &lt;em>open, secure, ad-free alternative to consumer IM services like AIM, ICQ, MSN, and Yahoo&lt;/em> (quoted from their site).&lt;/p>
&lt;p>A particular benefit of choosing an open platform is that it takes advantage of available client GUIs for instance, and moreover, it can leverage specific and standardized extensions of the XMPP protocol &amp;ndash; called JEPs. The Jabber foundation developed an interesting set of complementary protocol extensions by allowing custom XML payloads to be developed. And there&amp;rsquo;s one JEP of interest for us today: &lt;a href="http://www.jabber.org/jeps/jep-0009.html">JEP-0009&lt;/a>. This JEP defines a method for transporting XML-RPC encoded requests and responses over Jabber/XMPP.&lt;/p></description><content:encoded>
<![CDATA[<p>With the recent release of <a href="http://www.google.com/talk/">Google Talk</a>, the fine chaps at Google entered the Instant Messenging market. The most clever step in that direction was their choice of protocol for their IM solution: <a href="http://www.xmpp.org/">XMPP</a>. XMPP was popularized and standardized through the IETF by the <a href="http://www.jabber.org/">Jabber</a> software foundation with its famous <em>open, secure, ad-free alternative to consumer IM services like AIM, ICQ, MSN, and Yahoo</em> (quoted from their site).</p>
<p>A particular benefit of choosing an open platform is that it takes advantage of available client GUIs for instance, and moreover, it can leverage specific and standardized extensions of the XMPP protocol &ndash; called JEPs. The Jabber foundation developed an interesting set of complementary protocol extensions by allowing custom XML payloads to be developed. And there&rsquo;s one JEP of interest for us today: <a href="http://www.jabber.org/jeps/jep-0009.html">JEP-0009</a>. This JEP defines a method for transporting XML-RPC encoded requests and responses over Jabber/XMPP.</p>
<p><a href="http://www.jivesoftware.org/">JiveSoftware</a> developed a Jabber library called <a href="http://www.jivesoftware.org/smack/">Smack</a> that you can use to &ldquo;speak XMPP&rdquo;! They recently <a href="http://www.jivesoftware.org/smack/smack_gtalk.jsp">improved this library</a> to support Google Talk pecularities (TLS/SSL connection). With this library, we can also build our own payloads to support JEP-0009 and to programmatically make RPC calls.</p>
<p>Final point to glue everything together: my brilliant friend <a href="http://www.wilson.co.uk/">John Wilson</a>, who wrote the <a href="http://groovy.codehaus.org/Groovy+XML-RPC">Groovy XML-RPC</a> module, built upon the Smack library and its XML-RPC code to add XML-RPC support through Google talk! Groovy&rsquo;s XML-RPC is particularly easy to use to expose XML-RPC services through some clever use of closures in Groovy. That means it&rsquo;s now possible to do <a href="http://groovy.codehaus.org/Groovy+Jabber-RPC">remote procedure calls through Jabber</a>.</p>
<p>After all these presentations, it&rsquo;s high time to hack some code, isn&rsquo;t it? Let&rsquo;s create a simple echo service, which will simply echoes whatever we send to it.</p>
<p>Let&rsquo;s write the server:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.net.xmlrpc.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.jivesoftware.smack.GoogleTalkConnection</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> srv <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> JabberRPCServer<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// here is our echo service, that&#39;s a simple closure method!
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>srv<span style="color:#666">.</span><span style="color:#4070a0">echo</span> <span style="color:#666">=</span> <span style="color:#666">{</span> <span style="color:#007020;font-weight:bold">return</span> it <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// let&#39;s start the server
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> serverConnection <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> GoogleTalkConnection<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>serverConnection<span style="color:#666">.</span><span style="color:#4070a0">login</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;serverId&#34;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#34;serverPassword&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>srv<span style="color:#666">.</span><span style="color:#4070a0">startServer</span><span style="color:#666">(</span>serverConnection<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// let the server run for 60 seconds
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#007020;font-weight:bold">while</span><span style="color:#666">(</span><span style="color:#007020;font-weight:bold">true</span><span style="color:#666">)</span> <span style="color:#666">{</span> sleep <span style="color:#40a070">60</span><span style="color:#666">*</span><span style="color:#40a070">1000</span> <span style="color:#666">}</span>
</span></span></code></pre></div><p>Now that we&rsquo;ve written the server, it&rsquo;s time to create our client:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">groovy.net.xmlrpc.*</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.jivesoftware.smack.GoogleTalkConnection</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// let&#39;s connect to Google Talk
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> clientConnection <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> GoogleTalkConnection<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>clientConnection<span style="color:#666">.</span><span style="color:#4070a0">login</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;clientId&#34;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#34;clientPassword&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// create a transparent proxy around the remote service 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> serverProxy <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> JabberRPCServerProxy<span style="color:#666">(</span>clientConnection<span style="color:#666">,</span> <span style="color:#4070a0">&#34;serverId@gmail.com&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// it&#39;s time to call our service as if we were manipulating a local object
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>println serverProxy<span style="color:#666">.</span><span style="color:#4070a0">echo</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;Hello World!&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>println serverProxy<span style="color:#666">.</span><span style="color:#4070a0">echo</span><span style="color:#666">(</span><span style="color:#40a070">345</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>Currently, the improved Groovy XML-RPC library is in CVS Head only, so if you&rsquo;re impatient and can&rsquo;t wait for a new Groovy release, you&rsquo;ll have to build it yourself from sources! You propably also noticed you need two GTalk buddies who&rsquo;ve authorized each other to make this sample work.</p>
<p>A good question to ask yourself is <em>&ldquo;uh, well, cool, but what can I do with that?&rdquo;</em> Here&rsquo;s a potential use case John told me about where you could want to use Groovy&rsquo;s XML-RPC support over Google Talk :</p>
<blockquote>
<p>Some people want to use VNC to manage their home server, however you do not want to open a port on your firewall to allow VNC access from the internet (VNC is not very secure). The VNC server will &ldquo;reverse connect&rdquo; to an external client. So you could run a jabber RPC server which launched the VNC server telling it to reverse connect to your external machine</p></blockquote>
<p>I can also imagine some other use cases, like:</p>
<ul>
<li>triggering Continuous Integration servers which would support Jabber&rsquo;s XML-RPC support</li>
<li>doing some home automation to make you house warm when you come back from work in a cold winter</li>
<li>etc.</li>
</ul>
<p>There are many possibilities, and I&rsquo;m sure others will find some clever use cases for that. I can imagine Google Talk providing some cool services as well in the future, and I&rsquo;m pretty sure they will.</p>
<p>You may also wonder why not using raw XML-RPC or standard SOAP for these kind of services? That&rsquo;s another possibility, of course. It&rsquo;s up to you to use whatever protocol you want after all, but I thought it&rsquo;d be interesting to show you how we can leverage Google&rsquo;s own Jabber infrastructure. Moreover, you don&rsquo;t have to open a port on your firewall to allow access to a given port (safe against port scanning), nor you have to have a DNS entry for the machine hosting the service: all goes through the Google Talk server.</p>
<p>I hope you enjoyed this little funny toy. It may have brought to you a glimpse of what services Google could provide through its open and extensible platform.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Talking about Google Talk...</title><link>https://glaforge.dev/posts/2005/08/24/talking-about-google-talk/</link><pubDate>Wed, 24 Aug 2005 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2005/08/24/talking-about-google-talk/</guid><description>&lt;p>Okay, &lt;a href="http://www.google.fr/">Google&lt;/a> released its &lt;a href="http://www.google.com/talk/">Google Talk&lt;/a> client and its related services. This all sounds good and well, but alas, for us, poor corporate users, we have yet to figure out if it&amp;rsquo;ll ever work through our nasty proxies (with authentication) and firewalls (port 5222 should be opened?). I&amp;rsquo;ve tried tweaking the proxy settings myself, but it didn&amp;rsquo;t work. Unfortunately.&lt;/p>
&lt;p>On paper, Google Talk certainly looks promising, taking into account the great services Google have come up with so far, but there&amp;rsquo;s really not much more than other competitors already provide. And in fact, we have yet to see the great features not available anywhere that we all expect from Google. We&amp;rsquo;ve become pretty demanding users&amp;hellip; (and as we say in French &amp;ldquo;Qui aime bien châtie bien&amp;rdquo;).&lt;/p></description><content:encoded>
<![CDATA[<p>Okay, <a href="http://www.google.fr/">Google</a> released its <a href="http://www.google.com/talk/">Google Talk</a> client and its related services. This all sounds good and well, but alas, for us, poor corporate users, we have yet to figure out if it&rsquo;ll ever work through our nasty proxies (with authentication) and firewalls (port 5222 should be opened?). I&rsquo;ve tried tweaking the proxy settings myself, but it didn&rsquo;t work. Unfortunately.</p>
<p>On paper, Google Talk certainly looks promising, taking into account the great services Google have come up with so far, but there&rsquo;s really not much more than other competitors already provide. And in fact, we have yet to see the great features not available anywhere that we all expect from Google. We&rsquo;ve become pretty demanding users&hellip; (and as we say in French &ldquo;Qui aime bien châtie bien&rdquo;).</p>
<p>Apart from the integration with <a href="http://gmail.google.com/">GMail</a>, there&rsquo;s nothing really innovative that other services like <a href="http://fr.messenger.yahoo.com/">Yahoo! Messenger</a> or <a href="http://www.skype.com/">Skype</a> (doesn&rsquo;t seem to work across firewalls either) don&rsquo;t already provide. And worse, in their email invitations they send, they claim:</p>
<blockquote>
<p>Google Talk also works across all firewalls.</p></blockquote>
<p>but I have yet to see it becoming a true statement&hellip; But if ever it really manages to work everywhere, then that&rsquo;s becoming interesting to have a reliable Jabber server to play with, and that would be even greater if we could also get an (Java) API giving access to the notification means of the client. I guess we could do some cool things (Continuous Integration notifications, and so on). Time will tell&hellip;</p>
<p>In the meantime, if someone can tell me how to use it from work, I&rsquo;m game, otherwise, I think I&rsquo;ll stay with my good old and faithful <a href="http://fr.messenger.yahoo.com/">Yahoo! Messenger</a> which has always been working at work like a charm (except once where the company blocked *.yahoo.*).</p>
<p><strong>Update #1</strong>: It seems that Google is already aware of the problem I&rsquo;m facing with regards to passing through corporate firewalls. They had this to answer my information request:</p>
<blockquote>
<p>Thanks for contacting us about this problem. We&rsquo;re aware of the issue, and our engineers are working diligently to find a solution.</p></blockquote>
<p><strong>Update #2</strong>: Google Talk&rsquo;s client must probably not be working across all firewall configurations. The proof is that other clients like Psi work perfectly with Google Talk from work. So it really must be the client which&rsquo;s got some problems currently.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Spring in French: c'est le printemps !</title><link>https://glaforge.dev/posts/2005/08/23/spring-in-french-c-est-le-printemps/</link><pubDate>Tue, 23 Aug 2005 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2005/08/23/spring-in-french-c-est-le-printemps/</guid><description>&lt;p>At out last &lt;a href="http://ossgtp.xwiki.org/xwiki/bin/view/Main/18Aout2005">OSS-Get Together&lt;/a> meeting in Paris last thursday, we&amp;rsquo;ve had the chance to have &lt;a href="http://templth.blogspot.com/">Thierry Templier&lt;/a>, a &lt;a href="http://glaforge.free.fr/weblog/index.php?itemid=139">Spring modules&lt;/a> and a &lt;a href="http://jencks.org/">Jencks&lt;/a> commiter, make an introductory presentation of the &lt;a href="http://www.springframework.org/">Spring Framework&lt;/a>&lt;/p>
&lt;p>On our wiki, he made available two powerpoints in French:&lt;/p>
&lt;ul>
&lt;li>a big &lt;a href="http://ossgtp.xwiki.org/xwiki/bin/download/Main/18Aout2005/PRES+SPRING+OSSGTP.PPT">118-slides presentation on Spring&lt;/a>,&lt;/li>
&lt;li>and a specific one on &lt;a href="http://ossgtp.xwiki.org/xwiki/bin/download/Main/18Aout2005/PRES+SPRING+JCA+OSSGTP.PPT">Spring and JCA&lt;/a>.&lt;/li>
&lt;/ul>
&lt;p>That&amp;rsquo;s a lot of content to read for those of you speaking Molière&amp;rsquo;s tongue.&lt;/p>
&lt;p>Moreover, on his blog, he&amp;rsquo;s got a very interesting entry explaining how to &lt;a href="http://templth.blogspot.com/2005/08/use-groovy-with-spring.html">inject Groovy beans&lt;/a> with Spring or how to &lt;a href="http://templth.blogspot.com/2005/08/access-cics-with-spring.html">access CICS transactions&lt;/a> from Spring!&lt;/p></description><content:encoded>
<![CDATA[<p>At out last <a href="http://ossgtp.xwiki.org/xwiki/bin/view/Main/18Aout2005">OSS-Get Together</a> meeting in Paris last thursday, we&rsquo;ve had the chance to have <a href="http://templth.blogspot.com/">Thierry Templier</a>, a <a href="http://glaforge.free.fr/weblog/index.php?itemid=139">Spring modules</a> and a <a href="http://jencks.org/">Jencks</a> commiter, make an introductory presentation of the <a href="http://www.springframework.org/">Spring Framework</a></p>
<p>On our wiki, he made available two powerpoints in French:</p>
<ul>
<li>a big <a href="http://ossgtp.xwiki.org/xwiki/bin/download/Main/18Aout2005/PRES+SPRING+OSSGTP.PPT">118-slides presentation on Spring</a>,</li>
<li>and a specific one on <a href="http://ossgtp.xwiki.org/xwiki/bin/download/Main/18Aout2005/PRES+SPRING+JCA+OSSGTP.PPT">Spring and JCA</a>.</li>
</ul>
<p>That&rsquo;s a lot of content to read for those of you speaking Molière&rsquo;s tongue.</p>
<p>Moreover, on his blog, he&rsquo;s got a very interesting entry explaining how to <a href="http://templth.blogspot.com/2005/08/use-groovy-with-spring.html">inject Groovy beans</a> with Spring or how to <a href="http://templth.blogspot.com/2005/08/access-cics-with-spring.html">access CICS transactions</a> from Spring!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>The Guru of Groovy shares his Thoughts</title><link>https://glaforge.dev/posts/2005/08/11/the-guru-of-groovy-shares-his-thoughts/</link><pubDate>Thu, 11 Aug 2005 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2005/08/11/the-guru-of-groovy-shares-his-thoughts/</guid><description>&lt;p>As stated on Javalobby: &amp;ldquo;&lt;a href="http://javalobby.org/java/forums/t43566.html">The Guru of Groovy shares his Thoughts&lt;/a>&amp;rdquo;! Well, it seems like I&amp;rsquo;m that Guru! And that&amp;rsquo;s been my first official interview. That was a quite fun and interesting exchange of mails between Andrew and me, and it allowed me to develop a few interesting points I never had time to explain.&lt;/p>
&lt;p>Andrew Glover, &lt;a href="http://www.vanwardtechnologies.com/welcome.php">Vanward Technologies&lt;/a>&amp;rsquo; CTO, is a fan of &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a>. He wrote several great articles on DeveloperWorks in a &amp;ldquo;&lt;a href="http://www-128.ibm.com/developerworks/views/java/libraryview.jsp?search_by=practically+groovy:">Practically Groovy&amp;rdquo; series&lt;/a>&amp;quot;. I&amp;rsquo;ve recently had the pleasure to be &lt;a href="http://www.vanwardtechnologies.com/glaforge01.php">interviewed by him&lt;/a>regarding Groovy, and here&amp;rsquo;s what the abstract says:&lt;/p></description><content:encoded>
<![CDATA[<p>As stated on Javalobby: &ldquo;<a href="http://javalobby.org/java/forums/t43566.html">The Guru of Groovy shares his Thoughts</a>&rdquo;! Well, it seems like I&rsquo;m that Guru! And that&rsquo;s been my first official interview. That was a quite fun and interesting exchange of mails between Andrew and me, and it allowed me to develop a few interesting points I never had time to explain.</p>
<p>Andrew Glover, <a href="http://www.vanwardtechnologies.com/welcome.php">Vanward Technologies</a>&rsquo; CTO, is a fan of <a href="http://groovy.codehaus.org/">Groovy</a>. He wrote several great articles on DeveloperWorks in a &ldquo;<a href="http://www-128.ibm.com/developerworks/views/java/libraryview.jsp?search_by=practically+groovy:">Practically Groovy&rdquo; series</a>&quot;. I&rsquo;ve recently had the pleasure to be <a href="http://www.vanwardtechnologies.com/glaforge01.php">interviewed by him</a>regarding Groovy, and here&rsquo;s what the abstract says:</p>
<blockquote>
<p>As the defacto Guru of Groovy, Guillaume Laforge has guided the project through the ambitious JSR migration as well as setting the tone and the climate for the Groovy team. Read on as Guillaume shares his experiences as Groovy&rsquo;s governor, his thoughts on Groovy&rsquo;s future and why you should consider giving it a spin.</p></blockquote>
<p>So if you want to know a bit more about Groovy&rsquo;s history, present and future, or how I got involved in Groovy, go <a href="http://www.vanwardtechnologies.com/glaforge01.php">read it</a>!</p>
<p><strong>Update #1:</strong> The interview is also featured on <a href="http://www.theserverside.com/news/thread.tss?thread_id=35851">TheserverSide</a>.</p>
<p><strong>Update #2:</strong> On <a href="http://today.java.net/pub/n/FutureofGroovyInt">java.net</a> as well</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Wifi Rabbit for Continuous Integration</title><link>https://glaforge.dev/posts/2005/07/24/wifi-rabbit-for-continuous-integration/</link><pubDate>Sun, 24 Jul 2005 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2005/07/24/wifi-rabbit-for-continuous-integration/</guid><description>&lt;p>Call me a geek, but I&amp;rsquo;d really love to buy me one of these little &lt;a href="http://www.nabaztag.com/">Wifi rabbits&lt;/a>. The Nabaztag rabbit is a 23-cm high white rabbit with moving ears, and a set of flash LEDs of different colors. You can pair it with another rabbit so that when you move the first one&amp;rsquo;s ears, will automatically make the other one move its ears accordingly, even if your in another town or country (as long as you have a permanent DSL connection). Nabaztag can sing songs, or through some service subscription, he can react to the stock market trends, or to the weather, flashing in yellow when the sun is shinning.&lt;/p></description><content:encoded>
<![CDATA[<p>Call me a geek, but I&rsquo;d really love to buy me one of these little <a href="http://www.nabaztag.com/">Wifi rabbits</a>. The Nabaztag rabbit is a 23-cm high white rabbit with moving ears, and a set of flash LEDs of different colors. You can pair it with another rabbit so that when you move the first one&rsquo;s ears, will automatically make the other one move its ears accordingly, even if your in another town or country (as long as you have a permanent DSL connection). Nabaztag can sing songs, or through some service subscription, he can react to the stock market trends, or to the weather, flashing in yellow when the sun is shinning.</p>
<p>After our recent <a href="http://ossgtp.xwiki.com/xwiki/bin/view/Main/21Juillet2005">OSS-GT meeting</a> with talks on AOP (by Alexandre Vasseur from AspectJ/AspectWerkz) and CruiseControl (by Jérôme Lacoste, a CC contributor), it reminded me about <a href="http://www.clarkware.com/cgi/blosxom">Mike Clark</a>&rsquo;s book on<a href="http://pragmaticprogrammer.com/starter_kit/au/index.html">Pragmatic project automation</a> &ndash; that I need to order some day. In this book, Mike is talking about lava lamps to show that a build of your project failed, when your Continuous Integration server tries to build your project. I wonder if we can use that 95 euros Nabaztag to react upon build success or failure. Since Nabaztag can react upon certain emails containing particular keywords, I guess we could make glow red when the build fails, and green when it builds successfully.</p>
<p>That&rsquo;s a pretty neat geek gadget! And I&rsquo;ve found many other ones on the <a href="http://ww1.mageekstore.com/">Mageekstore</a> (content in French), like an <a href="http://www.mageekstore.com/boutique/fiche_produit.cfm?type=5&amp;ref=MTRMP3&amp;code_lg=lg_fr&amp;pag=1&amp;num=0">USB/MP3 player watch</a>, various USB lights, <a href="http://www.mageekstore.com/boutique/fiche_produit.cfm?type=1&amp;ref=SWMEM&amp;code_lg=lg_fr&amp;pag=1&amp;num=0">Swiss army-knives</a> with integrated USB key, or a <a href="http://www.mageekstore.com/boutique/fiche_produit.cfm?type=5&amp;ref=CUPWR&amp;code_lg=lg_fr&amp;pag=1&amp;num=0">coffee mug USB warmer</a>&hellip; Too many funny toys!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Aquarelle de Versailles</title><link>https://glaforge.dev/posts/2005/07/03/aquarelle-de-versailles/</link><pubDate>Sun, 03 Jul 2005 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2005/07/03/aquarelle-de-versailles/</guid><description>&lt;p>Pour mon anniversaire, Stéphanie m&amp;rsquo;a offert du matériel pour faire de l&amp;rsquo;aquarelle : un carnet à dessin et un coffret avec 16 couleurs. Comme le temps était plutôt agréable et clément ce week-end, nous avons décidé de pique-niquer auprès du Grand Canal du Château de Versailles. J&amp;rsquo;ai emmené tout mon attirail, et j&amp;rsquo;ai réalisé ma première aquarelle, que voici :&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-f253cb54e73470137169ebe1d93b32a0">
&lt;img src="https://glaforge.dev/img/misc/versailles-aquarelle.jpg"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-f253cb54e73470137169ebe1d93b32a0">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/versailles-aquarelle.jpg"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p></description><content:encoded>
<![CDATA[<p>Pour mon anniversaire, Stéphanie m&rsquo;a offert du matériel pour faire de l&rsquo;aquarelle : un carnet à dessin et un coffret avec 16 couleurs. Comme le temps était plutôt agréable et clément ce week-end, nous avons décidé de pique-niquer auprès du Grand Canal du Château de Versailles. J&rsquo;ai emmené tout mon attirail, et j&rsquo;ai réalisé ma première aquarelle, que voici :</p>
<p><figure>
  <a href="#img-f253cb54e73470137169ebe1d93b32a0">
    <img src="/img/misc/versailles-aquarelle.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-f253cb54e73470137169ebe1d93b32a0">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/versailles-aquarelle.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Initial release of the GroovyJ IntelliJ plugin!</title><link>https://glaforge.dev/posts/2005/07/01/initial-release-of-the-groovyj-intellij-plugin/</link><pubDate>Fri, 01 Jul 2005 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2005/07/01/initial-release-of-the-groovyj-intellij-plugin/</guid><description>&lt;p>Franck Rasolo, our IntelliJ expert, just released and announced the inital version of the GroovyJ IntelliJ IDEA plugin!&lt;/p>
&lt;blockquote>
&lt;p>The Groovy team is pleased to announce the first public release of GroovyJ, a plug-in that integrates the Groovy language into IntelliJ IDEA.&lt;/p>
&lt;p>You may browse the current &lt;a href="http://groovy.codehaus.org/GroovyJ+Status">GroovyJ status page&lt;/a> which lists the features shipped with this initial release.&lt;/p>
&lt;p>In a nutshell, with GroovyJ:&lt;/p>
&lt;ul>
&lt;li>a default Groovy runtime is automatically installed as a global library&lt;/li>
&lt;li>Groovy files can be edited with some amount of syntax highlighting&lt;/li>
&lt;li>&amp;lsquo;Run Configurations&amp;rsquo; can be created for runnable Groovy scripts&lt;/li>
&lt;li>Groovy scripts can be run with the output being captured in the embedded console window&lt;/li>
&lt;li>Groovy scripts located under module source/test folders will be automatically compiled when compiling modules&lt;/li>
&lt;/ul>
&lt;p>Note: This plug-in is available through IDEA&amp;rsquo;s plug-in manager and requires IntelliJ IDEA 5.0 build #3378 or higher.&lt;/p></description><content:encoded>
<![CDATA[<p>Franck Rasolo, our IntelliJ expert, just released and announced the inital version of the GroovyJ IntelliJ IDEA plugin!</p>
<blockquote>
<p>The Groovy team is pleased to announce the first public release of GroovyJ, a plug-in that integrates the Groovy language into IntelliJ IDEA.</p>
<p>You may browse the current <a href="http://groovy.codehaus.org/GroovyJ+Status">GroovyJ status page</a> which lists the features shipped with this initial release.</p>
<p>In a nutshell, with GroovyJ:</p>
<ul>
<li>a default Groovy runtime is automatically installed as a global library</li>
<li>Groovy files can be edited with some amount of syntax highlighting</li>
<li>&lsquo;Run Configurations&rsquo; can be created for runnable Groovy scripts</li>
<li>Groovy scripts can be run with the output being captured in the embedded console window</li>
<li>Groovy scripts located under module source/test folders will be automatically compiled when compiling modules</li>
</ul>
<p>Note: This plug-in is available through IDEA&rsquo;s plug-in manager and requires IntelliJ IDEA 5.0 build #3378 or higher.</p></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>JavaOne: Groovy reactions!</title><link>https://glaforge.dev/posts/2005/06/30/javaone-groovy-reactions/</link><pubDate>Thu, 30 Jun 2005 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2005/06/30/javaone-groovy-reactions/</guid><description>&lt;p>The &lt;a href="http://java.sun.com/javaone/">JavaOne&lt;/a> crowd is applauding &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a>! And the blogosphere is full of friendly comments towards our dynamic and agile scripting language for the JVM.&lt;/p>
&lt;p>&lt;a href="http://www.javanicus.com/blog2/">Jeremy Rayner&lt;/a> (our co-&lt;a href="http://www.jcp.org/en/jsr/detail?id=241">JSR-241&lt;/a> spec lead, and JSR project lead) compiled a list of blog posts related to Rod Cope&amp;rsquo;s JavaOne presentation, titled &amp;ldquo;Groovy = Java Technology + Ruby + Python for the JVM&amp;rdquo;. Rod Cope, &lt;a href="http://www.openlogic.com/">OpenLogic&lt;/a>&amp;rsquo;s CTO and Founder, has already given a few talks about Groovy, and evangelizes its use at various events. But he doesn&amp;rsquo;t only &amp;ldquo;speak&amp;rdquo; about Groovy, he&amp;rsquo;s also eating his own dog food, because his company flagship product &lt;a href="http://www.openlogic.com/page.php?pname=software/overview#">BlueGlue&lt;/a> embeds over 100,000 lines of Groovy code!&lt;/p></description><content:encoded>
<![CDATA[<p>The <a href="http://java.sun.com/javaone/">JavaOne</a> crowd is applauding <a href="http://groovy.codehaus.org/">Groovy</a>! And the blogosphere is full of friendly comments towards our dynamic and agile scripting language for the JVM.</p>
<p><a href="http://www.javanicus.com/blog2/">Jeremy Rayner</a> (our co-<a href="http://www.jcp.org/en/jsr/detail?id=241">JSR-241</a> spec lead, and JSR project lead) compiled a list of blog posts related to Rod Cope&rsquo;s JavaOne presentation, titled &ldquo;Groovy = Java Technology + Ruby + Python for the JVM&rdquo;. Rod Cope, <a href="http://www.openlogic.com/">OpenLogic</a>&rsquo;s CTO and Founder, has already given a few talks about Groovy, and evangelizes its use at various events. But he doesn&rsquo;t only &ldquo;speak&rdquo; about Groovy, he&rsquo;s also eating his own dog food, because his company flagship product <a href="http://www.openlogic.com/page.php?pname=software/overview#">BlueGlue</a> embeds over 100,000 lines of Groovy code!</p>
<p>Here&rsquo;s the list compiled by Jeremy:</p>
<ul>
<li><a href="http://www.artima.com/weblogs/viewpost.jsp?thread=116723">Artima</a>&rsquo;s Eric Armstrong proposed a thourough article on Groovy, saying JavaOne&rsquo;s day One was a Groovy day!</li>
<li><a href="http://www.theserverside.com/articles/article.tss?l=JavaOne_Day1">The Server Side</a> also talked about the presentation and it seemed that my <a href="http://glaforge.free.fr/weblog/index.php?itemid=101">Scriptom</a> (my pet sub-project and creation) yielded Wooww&rsquo;s and Aahh&rsquo;s moments in the audience, when Rod controlled an Excel spreadsheet from a groovysh console! Awesome!</li>
<li>Even funnier, a<a href="http://blogs.msdn.com/ericgu/archive/2004/07/08/177351.aspx">microsoftie</a> talked about Groovy!</li>
<li>Dion, a long time Groovy affictionado, <a href="http://www.almaer.com/blog/archives/000262.html">discusses</a> Microsoft&rsquo;s Eric Gunnerson comments</li>
<li>Ed Burns seems to be also <a href="http://weblogs.java.net/blog/edburns/archive/2005/06/ed_burns_javaon_2.html">impressed</a> with Groovy&rsquo;s terseness.</li>
<li>ClientJava.com is having his <a href="http://www.clientjava.com/blog/2005/06/27/1119917179424.html">&ldquo;whoa cool&rdquo; moment</a> while staring at Rod&rsquo;s Excel manipulation through Scriptom. And a user even commented that: <em>&ldquo;There is probably no specific reason you&rsquo;d have to use Groovy besides the &ldquo;cleaness&rdquo; that you mention it provides&rdquo;</em>.</li>
<li>Finally, Ryan Daigle talks about two nice use cases regarding how to leverage <a href="http://ryandaigle.com/pebble/2005/06/27/1119913090784.html">Ant</a> from Groovy, and how you can easily <a href="http://ryandaigle.com/pebble/2005/06/27/1119913775584.html">script ActiveX/COM components</a></li>
</ul>
<p>Overall, it seemed everybody was pretty entertained by Rod&rsquo;s great presentation, and enthousiasted by the progresses Groovy made over a year of hard and passionate work. I&rsquo;m impatiently looking forward to the day we&rsquo;ll release our first final release!</p>
<p>Groovy&rsquo;s more and more used in production settings, like Rod&rsquo;s BlueGlue, or Ludovic Dubost&rsquo;s <a href="http://www.xwiki.org/">XWiki</a> which hosts a farm of over 3500 Groovy-powered wikis with well over 600,000 page views per month. And we&rsquo;ve got some financial companies which already use or are planning to use Groovy to allow its business analysts to customize their apps&rsquo; behavior and business logic.</p>
<p>JavaOne&rsquo;s day One was a Groovy Day, and we can forsee a bright future for Groovy! Mustang&rsquo;s betas incorporate the JSR-223 scripting APIs that Groovy totally supports, so they may even ship the next betas with Groovy if Sun wanted. A big name company (I can&rsquo;t name yet) should probably talk soon about how they leverage Groovy to control and administer clusters of application servers&hellip; And there&rsquo;s more to come, so stay tuned and <a href="http://groovy.codehaus.org/Download">download Groovy</a> ASAP!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Maven Developer's Notebook</title><link>https://glaforge.dev/posts/2005/06/29/maven-developer-s-notebook/</link><pubDate>Wed, 29 Jun 2005 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2005/06/29/maven-developer-s-notebook/</guid><description>&lt;p>I&amp;rsquo;m glad I just received my &lt;a href="http://mavenbook.xwiki.com/xwiki/bin/view/Main/Book">Maven Developer&amp;rsquo;s Notebook&lt;/a>, from O&amp;rsquo;Reilly. My friend &lt;a href="http://blogs.codehaus.org/people/vmassol/">Vincent Massol&lt;/a> and Tim O&amp;rsquo;Brien co-authored this great book on &lt;a href="http://maven.apache.org/">Maven&lt;/a>, a definitely needed reading for all those who want to learn more about Maven, or who wants to get started quickly. I had the chance of being one of the reviewers and it allowed me to learn quite a few tricks in the process, at the same time I was converting one of my projects at work to a &amp;ldquo;mavenized&amp;rdquo; build process and integrating it inside &lt;a href="http://www.pmease.com/luntbuild/">Luntbuild&lt;/a> for continuous integration. I particularly liked the &amp;ldquo;best practices&amp;rdquo; aspects both authors managed to give throughout the different labs. This book is a must read, go and get it at your nearest bookstore!&lt;/p></description><content:encoded>
<![CDATA[<p>I&rsquo;m glad I just received my <a href="http://mavenbook.xwiki.com/xwiki/bin/view/Main/Book">Maven Developer&rsquo;s Notebook</a>, from O&rsquo;Reilly. My friend <a href="http://blogs.codehaus.org/people/vmassol/">Vincent Massol</a> and Tim O&rsquo;Brien co-authored this great book on <a href="http://maven.apache.org/">Maven</a>, a definitely needed reading for all those who want to learn more about Maven, or who wants to get started quickly. I had the chance of being one of the reviewers and it allowed me to learn quite a few tricks in the process, at the same time I was converting one of my projects at work to a &ldquo;mavenized&rdquo; build process and integrating it inside <a href="http://www.pmease.com/luntbuild/">Luntbuild</a> for continuous integration. I particularly liked the &ldquo;best practices&rdquo; aspects both authors managed to give throughout the different labs. This book is a must read, go and get it at your nearest bookstore!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy development: aiming for quality</title><link>https://glaforge.dev/posts/2005/06/19/groovy-development-aiming-for-quality/</link><pubDate>Sun, 19 Jun 2005 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2005/06/19/groovy-development-aiming-for-quality/</guid><description>&lt;p>A new &lt;a href="http://docs.codehaus.org/display/GROOVY/2005/06/15">milestone of Groovy was released&lt;/a> a few days ago. The general goal of this release was quality. 80-90% of our work was targeted at improving the user experience, as much as we could. One of the main complaints so far has always been regarding the ugly error reporting that Groovy was generating. Whether that be in the shell, or embededed, or when compiling your Groovy source code. It was pretty hard to figure out what the error was by looking at a meaningless message and hundreds of lines of exceptions.&lt;/p></description><content:encoded>
<![CDATA[<p>A new <a href="http://docs.codehaus.org/display/GROOVY/2005/06/15">milestone of Groovy was released</a> a few days ago. The general goal of this release was quality. 80-90% of our work was targeted at improving the user experience, as much as we could. One of the main complaints so far has always been regarding the ugly error reporting that Groovy was generating. Whether that be in the shell, or embededed, or when compiling your Groovy source code. It was pretty hard to figure out what the error was by looking at a meaningless message and hundreds of lines of exceptions.</p>
<p>So how did we improve that situation? Apart from cleaning the stacktraces both for compile-time or runtime errors, we&rsquo;ve worked hard on two fronts:</p>
<ul>
<li>Compile-time error reporting: a snippet of the offending code is shown, as well as a line and column number,</li>
<li>Compile-time checks: different checks are made to check variable definitions, or scoping rules.</li>
</ul>
<p>Rather than an ugly stacktrace, the following output is an improvement, judge for yourself:</p>
<pre tabindex="0"><code>D:\groovy\samples&gt;groovy errors.groovy
errors.groovy: 2: expecting anything but &#39;&#39;\n&#39;&#39;; got it anyway @ line 2, column 13.
   println &#34;foo
               ^

1 Error
</code></pre><p>Regarding the compile-time checks, we finally spent some time on implementing the checks we&rsquo;re used to have in the standard java toolchain:</p>
<p>Checks for classes and scripts:</p>
<ul>
<li>check no variable is defined more than once</li>
</ul>
<p>Checks for classes only:</p>
<ul>
<li>
<p>check every variable is defined before usage</p>
</li>
<li>
<p>check that abstract methods are implemented when the class is not abstract</p>
</li>
<li>
<p>check that no final methods are overwritten</p>
</li>
<li>
<p>check that no final class is used as direct parent</p>
</li>
<li>
<p>check that no access from a static context to a dynamic declared variable is possible, example:</p>
<pre tabindex="0"><code>class A { def var; def static m() { println var } }
</code></pre></li>
</ul>
<p>That&rsquo;s a real pleasure to see the progresses made these last months in the maturation of Groovy. With just a bunch of passionate developers, we can really do great things. And the recent improvements should pave the way of a successful alternative language for the JVM.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Lucene's fun</title><link>https://glaforge.dev/posts/2005/05/02/lucene-s-fun/</link><pubDate>Mon, 02 May 2005 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2005/05/02/lucene-s-fun/</guid><description>&lt;p>I played with Lucene today to index a CSV file representing the Unicode characters metadata. Lucene is so easy and fun to use, that&amp;rsquo;s really the kind of libraries I like very much. I took some inspiration from my friend Jeremy who &lt;a href="http://javanicus.com/blog2/items/178-index.html">played with Lucene and Groovy&lt;/a> recently, by translating into Groovy some examples of &lt;a href="http://www.lucenebook.com/">Lucene in Action&lt;/a>.&lt;/p>
&lt;p>I&amp;rsquo;ve always been interested in i18n issues, charset/encoding malarkey, and so on. It&amp;rsquo;s always a pain to deal with&amp;hellip; but it&amp;rsquo;s pretty damn interesting, and that&amp;rsquo;s often a problem that native English speakers overlook.&lt;/p></description><content:encoded>
<![CDATA[<p>I played with Lucene today to index a CSV file representing the Unicode characters metadata. Lucene is so easy and fun to use, that&rsquo;s really the kind of libraries I like very much. I took some inspiration from my friend Jeremy who <a href="http://javanicus.com/blog2/items/178-index.html">played with Lucene and Groovy</a> recently, by translating into Groovy some examples of <a href="http://www.lucenebook.com/">Lucene in Action</a>.</p>
<p>I&rsquo;ve always been interested in i18n issues, charset/encoding malarkey, and so on. It&rsquo;s always a pain to deal with&hellip; but it&rsquo;s pretty damn interesting, and that&rsquo;s often a problem that native English speakers overlook.</p>
<p>In a previous article, you learned how to remove diacritical marks from strings (in particular <a href="https://glaforge.dev/posts/2005/04/27/how-to-remove-accents-from-a-string/">removing accents</a>), and I wanted to know how the JDK or IBM&rsquo;s ICU4J know how to decompose strings in their canonical form. Of course that&rsquo;s done through some data file containing that information. You can find <a href="http://www.unicode.org/Public/UNIDATA/UnicodeData.txt">such a file</a> on the <a href="http://www.unicode.org/">Unicode website</a>. It contains information on each character regarding how to transform it to lowercase or uppercase, how to decompose it in its canonical form, or to learn its standard Unicode name.</p>
<p>I took this file, indexed its content and fields, and could easily use Lucene&rsquo;s search capabilities in no time to discover more about Unicode characters. As usual, here are some code snippets in Groovy!</p>
<p>So first of all, let&rsquo;s create an index out of that Unicode data file:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.apache.lucene.analysis.standard.StandardAnalyzer</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.apache.lucene.document.Document</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.apache.lucene.document.Field</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.apache.lucene.index.IndexWriter</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> writer <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> IndexWriter<span style="color:#666">(</span><span style="color:#007020;font-weight:bold">new</span> File<span style="color:#666">(</span><span style="color:#4070a0">&#34;index&#34;</span><span style="color:#666">),</span> <span style="color:#007020;font-weight:bold">new</span> StandardAnalyzer<span style="color:#666">(),</span> <span style="color:#007020;font-weight:bold">true</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">File</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;UnicodeData.txt&#34;</span><span style="color:#666">).</span><span style="color:#4070a0">eachLine</span> <span style="color:#666">{</span> line <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> ucharFields <span style="color:#666">=</span> line<span style="color:#666">.</span><span style="color:#4070a0">split</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;;&#39;</span><span style="color:#666">,</span> <span style="color:#40a070">15</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>ucharFields<span style="color:#666">[</span><span style="color:#40a070">0</span><span style="color:#666">].</span><span style="color:#4070a0">length</span><span style="color:#666">()</span> <span style="color:#666">==</span> <span style="color:#40a070">4</span><span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#902000">def</span> doc <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Document<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>        doc<span style="color:#666">.</span><span style="color:#4070a0">add</span><span style="color:#666">(</span>Field<span style="color:#666">.</span><span style="color:#4070a0">Keyword</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;code&#34;</span><span style="color:#666">,</span> ucharFields<span style="color:#666">[</span><span style="color:#40a070">0</span><span style="color:#666">]))</span>
</span></span><span style="display:flex;"><span>        doc<span style="color:#666">.</span><span style="color:#4070a0">add</span><span style="color:#666">(</span>Field<span style="color:#666">.</span><span style="color:#4070a0">Text</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;name&#34;</span><span style="color:#666">,</span> ucharFields<span style="color:#666">[</span><span style="color:#40a070">1</span><span style="color:#666">]))</span>
</span></span><span style="display:flex;"><span>        doc<span style="color:#666">.</span><span style="color:#4070a0">add</span><span style="color:#666">(</span>Field<span style="color:#666">.</span><span style="color:#4070a0">Keyword</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;category&#34;</span><span style="color:#666">,</span> ucharFields<span style="color:#666">[</span><span style="color:#40a070">2</span><span style="color:#666">]))</span>
</span></span><span style="display:flex;"><span>        doc<span style="color:#666">.</span><span style="color:#4070a0">add</span><span style="color:#666">(</span>Field<span style="color:#666">.</span><span style="color:#4070a0">Keyword</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;clazz&#34;</span><span style="color:#666">,</span> ucharFields<span style="color:#666">[</span><span style="color:#40a070">3</span><span style="color:#666">]))</span>
</span></span><span style="display:flex;"><span>        doc<span style="color:#666">.</span><span style="color:#4070a0">add</span><span style="color:#666">(</span>Field<span style="color:#666">.</span><span style="color:#4070a0">Keyword</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;bidi&#34;</span><span style="color:#666">,</span> ucharFields<span style="color:#666">[</span><span style="color:#40a070">4</span><span style="color:#666">]))</span>
</span></span><span style="display:flex;"><span>        doc<span style="color:#666">.</span><span style="color:#4070a0">add</span><span style="color:#666">(</span>Field<span style="color:#666">.</span><span style="color:#4070a0">Keyword</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;decomposition&#34;</span><span style="color:#666">,</span> ucharFields<span style="color:#666">[</span><span style="color:#40a070">5</span><span style="color:#666">]))</span>
</span></span><span style="display:flex;"><span>        doc<span style="color:#666">.</span><span style="color:#4070a0">add</span><span style="color:#666">(</span>Field<span style="color:#666">.</span><span style="color:#4070a0">Keyword</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;numeric&#34;</span><span style="color:#666">,</span> ucharFields<span style="color:#666">[</span><span style="color:#40a070">8</span><span style="color:#666">]))</span>
</span></span><span style="display:flex;"><span>        doc<span style="color:#666">.</span><span style="color:#4070a0">add</span><span style="color:#666">(</span>Field<span style="color:#666">.</span><span style="color:#4070a0">Keyword</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;mirrored&#34;</span><span style="color:#666">,</span> ucharFields<span style="color:#666">[</span><span style="color:#40a070">9</span><span style="color:#666">]))</span>
</span></span><span style="display:flex;"><span>        doc<span style="color:#666">.</span><span style="color:#4070a0">add</span><span style="color:#666">(</span>Field<span style="color:#666">.</span><span style="color:#4070a0">Keyword</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;uppercase&#34;</span><span style="color:#666">,</span> ucharFields<span style="color:#666">[</span><span style="color:#40a070">12</span><span style="color:#666">]))</span>
</span></span><span style="display:flex;"><span>        doc<span style="color:#666">.</span><span style="color:#4070a0">add</span><span style="color:#666">(</span>Field<span style="color:#666">.</span><span style="color:#4070a0">Keyword</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;lowercase&#34;</span><span style="color:#666">,</span> ucharFields<span style="color:#666">[</span><span style="color:#40a070">13</span><span style="color:#666">]))</span>
</span></span><span style="display:flex;"><span>        doc<span style="color:#666">.</span><span style="color:#4070a0">add</span><span style="color:#666">(</span>Field<span style="color:#666">.</span><span style="color:#4070a0">Keyword</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;titlecase&#34;</span><span style="color:#666">,</span> ucharFields<span style="color:#666">[</span><span style="color:#40a070">14</span><span style="color:#666">]))</span>
</span></span><span style="display:flex;"><span>        writer<span style="color:#666">.</span><span style="color:#4070a0">addDocument</span><span style="color:#666">(</span>doc<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>writer<span style="color:#666">.</span><span style="color:#4070a0">optimize</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>writer<span style="color:#666">.</span><span style="color:#4070a0">close</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>For each line of the document representing a character, I split it using &ldquo;;&rdquo; as the delimiter for the fields of the CSV. And I create a Lucene Document and adds some fields and content. You&rsquo;ll notice the different fields I&rsquo;m interested in: like the name of the character, its uppercase or lowercase equivalent, etc. You can find <a href="http://www.unicode.org/Public/UNIDATA/UCD.html">more information</a> about those fields in case you feel like learning more about that.</p>
<p>If you downloaded Lucene&rsquo;s jar and put both the jar and the UnicodeData.txt file in the same directory as your scripts, you can use the following command-line to create your index:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>groovy -cp lucene-1.4.3.jar UIndexer.groovy
</span></span></code></pre></div><p>It will take some time, but it will create an index subdirectory containing Lucene&rsquo;s index.</p>
<p>Now that you have your index ready, you can query it with the following script:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.apache.lucene.analysis.standard.StandardAnalyzer</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.apache.lucene.queryParser.QueryParser</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.apache.lucene.search.IndexSearcher</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.apache.lucene.store.FSDirectory</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> indexDir <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> File<span style="color:#666">(</span><span style="color:#4070a0">&#34;index&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> q <span style="color:#666">=</span> args<span style="color:#666">[</span><span style="color:#40a070">0</span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> fsDir <span style="color:#666">=</span> FSDirectory<span style="color:#666">.</span><span style="color:#4070a0">getDirectory</span><span style="color:#666">(</span>indexDir<span style="color:#666">,</span> <span style="color:#007020;font-weight:bold">false</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> is <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> IndexSearcher<span style="color:#666">(</span>fsDir<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> queryParser <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> QueryParser<span style="color:#666">(</span><span style="color:#4070a0">&#34;name&#34;</span><span style="color:#666">,</span> <span style="color:#007020;font-weight:bold">new</span> StandardAnalyzer<span style="color:#666">())</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> query <span style="color:#666">=</span> queryParser<span style="color:#666">.</span><span style="color:#4070a0">parse</span><span style="color:#666">(</span>q<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> start <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Date<span style="color:#666">().</span><span style="color:#4070a0">time</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> hits <span style="color:#666">=</span> is<span style="color:#666">.</span><span style="color:#4070a0">search</span><span style="color:#666">(</span>query<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> end <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> Date<span style="color:#666">().</span><span style="color:#4070a0">time</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>println <span style="color:#4070a0">&#34;Found ${hits.length()} characters in ${end - start}ms:\n&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">for</span> <span style="color:#666">(</span> i <span style="color:#007020;font-weight:bold">in</span> <span style="color:#40a070">0</span> <span style="color:#666">..&lt;</span> hits<span style="color:#666">.</span><span style="color:#4070a0">length</span><span style="color:#666">()</span> <span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> doc <span style="color:#666">=</span> hits<span style="color:#666">.</span><span style="color:#4070a0">doc</span><span style="color:#666">(</span>i<span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    println<span style="color:#666">(</span><span style="color:#4070a0">&#34;${doc[&#39;code&#39;]} () -&gt; ${doc[&#39;name&#39;]}&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    println<span style="color:#666">(</span><span style="color:#4070a0">&#34;       category: ${doc[&#39;category&#39;]}&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    println<span style="color:#666">(</span><span style="color:#4070a0">&#34;          class: ${doc[&#39;clazz&#39;]}&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    println<span style="color:#666">(</span><span style="color:#4070a0">&#34;           bidi: ${doc[&#39;bidi&#39;]}&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    println<span style="color:#666">(</span><span style="color:#4070a0">&#34;  decomposition: ${doc[&#39;decomposition&#39;]}&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    println<span style="color:#666">(</span><span style="color:#4070a0">&#34;        numeric: ${doc[&#39;numeric&#39;]}&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    println<span style="color:#666">(</span><span style="color:#4070a0">&#34;       mirrored: ${doc[&#39;mirrored&#39;]}&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    println<span style="color:#666">(</span><span style="color:#4070a0">&#34;      uppercase: ${doc[&#39;uppercase&#39;]}&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    println<span style="color:#666">(</span><span style="color:#4070a0">&#34;      lowercase: ${doc[&#39;lowercase&#39;]}&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    println<span style="color:#666">(</span><span style="color:#4070a0">&#34;      titlecase: ${doc[&#39;titlecase&#39;]}&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    println<span style="color:#666">(</span><span style="color:#4070a0">&#34;&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>It opens the index, and takes a query as its first command-line argument. It parses the query, and searches the index, and in the end, it will return some useful results.</p>
<p>For instance, you can make some queries to know which characters contain the word cedilla in their name:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>groovy -cp lucene-1.4.3.jar USearcher.groovy cedilla
</span></span></code></pre></div><p>And you can also search particular fields:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>groovy -cp lucene-1.4.3.jar USearcher.groovy <span style="color:#4070a0">&#34;category:Lu&#34;</span>
</span></span></code></pre></div><p>Those queries will print all the corresponding Unicode characters you&rsquo;re looking for.</p>
<p>As you can see through those two samples, creating the index, or searching the index is really simple and straightforward. Lucene does it jobs fairly well, and I just love it for that.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Maven scripting in Groovy</title><link>https://glaforge.dev/posts/2005/05/01/maven-scripting-in-groovy/</link><pubDate>Sun, 01 May 2005 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2005/05/01/maven-scripting-in-groovy/</guid><description>&lt;p>Recently, on Maven&amp;rsquo;s user list, someone wanted to be able to script Maven with Groovy, like it was already possible with BeanShell or Jython thanks to specific Jelly tags. But unfortunately, Maven didn&amp;rsquo;t provide that kind of support for Groovy.&lt;/p>
&lt;p>But there are good news on that front since &lt;a href="http://javanicus.com/blog2/">Jeremy Rayner&lt;/a> contributed a nice &lt;a href="http://groovy.codehaus.org/Groovy+Ant+Task">Groovy Ant task&lt;/a>.&lt;/p>
&lt;p>That Ant task will be available in the upcoming Groovy release (jsr-02), so if you want to play with Maven and Groovy, you&amp;rsquo;ll have to build Groovy from sources in the meantime. But I&amp;rsquo;m going to reveal how to script Maven with Groovy in advance.&lt;/p></description><content:encoded>
<![CDATA[<p>Recently, on Maven&rsquo;s user list, someone wanted to be able to script Maven with Groovy, like it was already possible with BeanShell or Jython thanks to specific Jelly tags. But unfortunately, Maven didn&rsquo;t provide that kind of support for Groovy.</p>
<p>But there are good news on that front since <a href="http://javanicus.com/blog2/">Jeremy Rayner</a> contributed a nice <a href="http://groovy.codehaus.org/Groovy+Ant+Task">Groovy Ant task</a>.</p>
<p>That Ant task will be available in the upcoming Groovy release (jsr-02), so if you want to play with Maven and Groovy, you&rsquo;ll have to build Groovy from sources in the meantime. But I&rsquo;m going to reveal how to script Maven with Groovy in advance.</p>
<p>In your <strong>maven.xml</strong>, you can access Maven&rsquo;s POM:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#007020">&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;project</span> <span style="color:#4070a0">default=</span><span style="color:#4070a0">&#34;groovy&#34;</span> <span style="color:#4070a0">xmlns:ant=</span><span style="color:#4070a0">&#34;jelly:ant&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>
</span></span><span style="display:flex;"><span> <span style="color:#062873;font-weight:bold">&lt;goal</span> <span style="color:#4070a0">name=</span><span style="color:#4070a0">&#34;groovy&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>
</span></span><span style="display:flex;"><span>   <span style="color:#062873;font-weight:bold">&lt;ant:taskdef</span> <span style="color:#4070a0">name=</span><span style="color:#4070a0">&#34;groovy&#34;</span> <span style="color:#4070a0">classname=</span><span style="color:#4070a0">&#34;org.codehaus.groovy.ant.Groovy&#34;</span> <span style="color:#4070a0">classpathref=</span><span style="color:#4070a0">&#34;maven.dependency.classpath&#34;</span><span style="color:#062873;font-weight:bold">/&gt;</span>
</span></span><span style="display:flex;"><span>   <span style="color:#062873;font-weight:bold">&lt;ant:groovy&gt;</span>
</span></span><span style="display:flex;"><span>       println pom.eachPropertyName{ println it }
</span></span><span style="display:flex;"><span>       println pom.name
</span></span><span style="display:flex;"><span>   <span style="color:#062873;font-weight:bold">&lt;/ant:groovy&gt;</span>
</span></span><span style="display:flex;"><span> <span style="color:#062873;font-weight:bold">&lt;/goal&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/project&gt;</span>
</span></span></code></pre></div><p>Don&rsquo;t forget to add Groovy in your dependencies:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span>   <span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>     <span style="color:#062873;font-weight:bold">&lt;groupId&gt;</span>groovy<span style="color:#062873;font-weight:bold">&lt;/groupId&gt;</span>
</span></span><span style="display:flex;"><span>     <span style="color:#062873;font-weight:bold">&lt;artifactId&gt;</span>groovy-all<span style="color:#062873;font-weight:bold">&lt;/artifactId&gt;</span>
</span></span><span style="display:flex;"><span>     <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>1.0-jsr-02-SNAPSHOT<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>     <span style="color:#062873;font-weight:bold">&lt;properties&gt;</span>
</span></span><span style="display:flex;"><span>       <span style="color:#062873;font-weight:bold">&lt;classloader&gt;</span>root<span style="color:#062873;font-weight:bold">&lt;/classloader&gt;</span>
</span></span><span style="display:flex;"><span>     <span style="color:#062873;font-weight:bold">&lt;/properties&gt;</span>
</span></span><span style="display:flex;"><span>   <span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div><p>Currently, only the POM is added in the script context, but other variables or properties may be added, but you can already access almost everything from Maven&rsquo;s project. Don&rsquo;t hesitate to ping me if you&rsquo;ve got some specific requests.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>How to remove accents from a String</title><link>https://glaforge.dev/posts/2005/04/27/how-to-remove-accents-from-a-string/</link><pubDate>Wed, 27 Apr 2005 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2005/04/27/how-to-remove-accents-from-a-string/</guid><description>&lt;p>My little puzzle of the day is to find how to remove accents from a String. There are different alternatives, different strategies, but none really suits my needs &amp;ndash; or my taste.&lt;/p>
&lt;p>The naive approach is to use &lt;code>String.replace()&lt;/code> to replace manually all characters, with a correspondance table, like &amp;ldquo;é&amp;rdquo; should be replaced with &amp;ldquo;e&amp;rdquo;, etc. That&amp;rsquo;s fine for some languages I know, like French or German, or even some latin languages, since we share the same alphabet. But with Russian, Greek, or some asian languages, my knowledge won&amp;rsquo;t suffice! So I can&amp;rsquo;t reliably produce a big hashtable with that knowledge. What a pity!&lt;/p></description><content:encoded>
<![CDATA[<p>My little puzzle of the day is to find how to remove accents from a String. There are different alternatives, different strategies, but none really suits my needs &ndash; or my taste.</p>
<p>The naive approach is to use <code>String.replace()</code> to replace manually all characters, with a correspondance table, like &ldquo;é&rdquo; should be replaced with &ldquo;e&rdquo;, etc. That&rsquo;s fine for some languages I know, like French or German, or even some latin languages, since we share the same alphabet. But with Russian, Greek, or some asian languages, my knowledge won&rsquo;t suffice! So I can&rsquo;t reliably produce a big hashtable with that knowledge. What a pity!</p>
<p>The other two approaches are using a <code>Normalizer</code> class which decomposes a string in its smallest constituents. Thus, a character with an accent is composed of a non-accentuated character and a diacritical mark. Then, once I have this expanded string, I can easily remove all characters representing a diacritical mark, because they all belong to a certain Unicode category.</p>
<p>Sun&rsquo;s JDK contains a non-public class called sun.text.Normalizer (which should be added to the JDK&rsquo;s public APIs), and <a href="http://icu.sourceforge.net/">IBM&rsquo;s ICU</a> (International Components for Unicode) package also contains such a class. The following function will return a string without accents or other marks:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span>String<span style="color:#bbb"> </span><span style="color:#06287e">removeAccents</span>(String<span style="color:#bbb"> </span>text)<span style="color:#bbb"> </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>Normalizer.<span style="color:#4070a0">decompose</span>(text,<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">false</span>,<span style="color:#bbb"> </span>0)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">                     </span>.<span style="color:#4070a0">replaceAll</span>(<span style="color:#4070a0">&#34;\p{InCombiningDiacriticalMarks}+&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>Using Sun&rsquo;s internal class is not a very portable solution, but using IBM&rsquo;s 3MB-gorilla JAR may be overkill for just removing accents. But after all, what are 3 megabytes when your machines have gigas of RAM and disk space? Would it slow your apps down? Probably not.</p>
<p>Does somebody know of another way to remove accents from a String?</p>
<p><strong>Update (2011/07/28):</strong> Sun&rsquo;s JDK 6 now includes a Normalizer class. So for example, if you want to transform the accentuated letters to their non-accentuated form, you can do this:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>Normalizer.<span style="color:#4070a0">normalize</span>(title,<span style="color:#bbb"> </span>Normalizer.<span style="color:#4070a0">Form</span>.<span style="color:#4070a0">NFD</span>)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">          </span>.<span style="color:#4070a0">replaceAll</span>(<span style="color:#4070a0">&#34;\p{InCombiningDiacriticalMarks}+&#34;</span>,<span style="color:#bbb"> </span><span style="color:#4070a0">&#34;&#34;</span>)<span style="color:#bbb">
</span></span></span></code></pre></div><p>Update (2012/03/14): George suggests in the comments a better category: \\p{IsM} which covers more combining marks than just accents.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Maven tip: using Ant's optional FTP task</title><link>https://glaforge.dev/posts/2005/04/26/maven-tip-using-ant-s-optional-ftp-task/</link><pubDate>Tue, 26 Apr 2005 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2005/04/26/maven-tip-using-ant-s-optional-ftp-task/</guid><description>&lt;p>&lt;a href="http://maven.apache.org/">Maven&lt;/a> is a pretty powerful tool, but sometimes, simple things can get complicated&amp;hellip; I had to customize my build to upload some files through FTP. But it wasn&amp;rsquo;t just a mere artifact to upload through FTP to the enterprise repository, so I couldn&amp;rsquo;t use Maven&amp;rsquo;s artifact plugin and its FTP method. So the solution was to use Ant&amp;rsquo;s optional FTP task.&lt;/p>
&lt;p>At first, it doesn&amp;rsquo;t seem very complicated, since Maven can basically use any Ant task very easily, but the fact is that this optional Ant task is dependent on another library that you have to add to Maven&amp;rsquo;s root classloader, otherwise you&amp;rsquo;ll get a NoClassDefFound! And moreover, I couldn&amp;rsquo;t even find NetComponents on ibiblio!&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://maven.apache.org/">Maven</a> is a pretty powerful tool, but sometimes, simple things can get complicated&hellip; I had to customize my build to upload some files through FTP. But it wasn&rsquo;t just a mere artifact to upload through FTP to the enterprise repository, so I couldn&rsquo;t use Maven&rsquo;s artifact plugin and its FTP method. So the solution was to use Ant&rsquo;s optional FTP task.</p>
<p>At first, it doesn&rsquo;t seem very complicated, since Maven can basically use any Ant task very easily, but the fact is that this optional Ant task is dependent on another library that you have to add to Maven&rsquo;s root classloader, otherwise you&rsquo;ll get a NoClassDefFound! And moreover, I couldn&rsquo;t even find NetComponents on ibiblio!</p>
<p>To save you that pain, please read the instructions below. It is inspired from <a href="http://java2.5341.com/msg/101580.html">a solution</a> I&rsquo;ve found by googling, but the problem is that it is not the cleanest solution, since you have to hack Maven to add the library in the root classpath, and modify Maven&rsquo;s own private configuration. That&rsquo;s not very portable! My friend <a href="http://blogs.codehaus.org/people/vmassol">Vincent Massol</a>, author of <a href="http://tinyurl.com/7hprk">JUnit in Action</a> and of the upcoming <a href="http://blogs.codehaus.org/people/vmassol/archives/001003_maven_book_and_maven_quiz.html">Maven Developer&rsquo;s Notebook</a>, helped me improve upon that solution to make it portable across different build environments, without requiring to modify Maven&rsquo;s own configuration.</p>
<p>Here are the instructions:</p>
<ul>
<li>
<p>Download NetComponents from: <a href="http://www.savarese.org/oro/downloads/NetComponents-1.3.8.tar.gz">http://www.savarese.org/oro/downloads/NetComponents-1.3.8.tar.gz</a></p>
</li>
<li>
<p>Uncompress the archive, take the NetComponent.jar and put it in a new directory:
<code>${user.home}/.maven/repository/net-components</code></p>
</li>
<li>
<p>Rename the jar to <code>NetComponents-1.3.8a.jar</code></p>
</li>
<li>
<p>Add the dependency in your <code>project.xml</code>:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;dependency&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;groupid&gt;</span>net-components<span style="color:#062873;font-weight:bold">&lt;/groupid&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;artifactid&gt;</span>NetComponents<span style="color:#062873;font-weight:bold">&lt;/artifactid&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;version&gt;</span>1.3.8a<span style="color:#062873;font-weight:bold">&lt;/version&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;b&gt;&lt;properties&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;classloader&gt;</span>root<span style="color:#062873;font-weight:bold">&lt;/classloader&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;/properties&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;/dependency&gt;</span>
</span></span></code></pre></div><p>The <code>root</code> is there to tell Maven to load that dependency with its root classloader, so that it is available to Maven&rsquo;s Ant which is loaded by that root classloader.</p>
</li>
<li>
<p>Now, you can add a goal in your <code>maven.xml</code> file to call the FTP task, and don&rsquo;t forget to create a task definition:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;project</span> <span style="color:#4070a0">default=</span><span style="color:#4070a0">&#34;java:compile&#34;</span> <span style="color:#4070a0">xmlns:ant=</span><span style="color:#4070a0">&#34;jelly:ant&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;goal</span> <span style="color:#4070a0">name=</span><span style="color:#4070a0">&#34;ftp&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;taskdef</span> <span style="color:#4070a0">name=</span><span style="color:#4070a0">&#34;ftp&#34;</span> <span style="color:#4070a0">classname=</span><span style="color:#4070a0">&#34;org.apache.tools.ant.taskdefs.optional.net.FTP&#34;</span><span style="color:#062873;font-weight:bold">&gt;&lt;/taskdef&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;ant:ftp</span> <span style="color:#4070a0">server=</span><span style="color:#4070a0">&#34;yourserver.domain.com&#34;</span> <span style="color:#4070a0">userid=</span><span style="color:#4070a0">&#34;foo&#34;</span> <span style="color:#4070a0">password=</span><span style="color:#4070a0">&#34;bar&#34;</span> <span style="color:#4070a0">action=</span><span style="color:#4070a0">&#34;list&#34;</span> <span style="color:#4070a0">listing=</span><span style="color:#4070a0">&#34;list.txt&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;fileset&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#062873;font-weight:bold">&lt;include</span> <span style="color:#4070a0">name=</span><span style="color:#4070a0">&#34;*.html&#34;</span><span style="color:#062873;font-weight:bold">&gt;&lt;/include&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#062873;font-weight:bold">&lt;/fileset&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;/ant:ftp&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;/goal&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/project&gt;</span>
</span></span></code></pre></div></li>
</ul>
<p>For the purpose of this example, the above goal lists the <code>*.html</code> files in your remote home folder. It will create a <code>list.txt</code> file containing the result of that listing. But of course, all FTP operations are available to you.</p>
<p>Now just type <code>maven ftp</code> and you&rsquo;re done! Was it that hard? Well, once you&rsquo;ve read that, it isn&rsquo;t, but still&hellip;</p>
<p>I hope it&rsquo;ll save you some precious minutes, if not hours if you&rsquo;re not a Maven guru.</p>
<p>For further information, here are some pointers:</p>
<ul>
<li><a href="http://java2.5341.com/msg/101580.html">Original solution</a></li>
<li><a href="http://ant.apache.org/manual/OptionalTasks/ftp.html">Ant&rsquo;s optional FTP task</a></li>
<li><a href="http://www.savarese.org/oro/downloads/NetComponents-1.3.8.tar.gz">NetComponents distribution</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Finding styled paragraphs in a Word document</title><link>https://glaforge.dev/posts/2005/04/24/finding-styled-paragraphs-in-a-word-document/</link><pubDate>Sun, 24 Apr 2005 02:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2005/04/24/finding-styled-paragraphs-in-a-word-document/</guid><description>&lt;p>When working with Word documents, you&amp;rsquo;d often like to be able to extract relevant content automatically thanks to some scripts. That&amp;rsquo;s exactly what I wanted to do this afternoon. I have a Word document which contains some scripts written with a specific style, and I wanted to extract those snippets, so that I may be able to check that they compile or that the unit tests they represent be asserted successfully.&lt;/p></description><content:encoded>
<![CDATA[<p>When working with Word documents, you&rsquo;d often like to be able to extract relevant content automatically thanks to some scripts. That&rsquo;s exactly what I wanted to do this afternoon. I have a Word document which contains some scripts written with a specific style, and I wanted to extract those snippets, so that I may be able to check that they compile or that the unit tests they represent be asserted successfully.</p>
<p>As usual, you need to choose the right tool for the job. And for me, the right tool was my lovely <a href="http://groovy.codehaus.org/COM+Scripting">Scriptom</a> module for <a href="http://groovy.codehaus.org/">Groovy</a>. Scriptom allows you to fill in the gap between Groovy scripts and your native ActiveX/COM components (on Windows obvioulsy).</p>
<p>I&rsquo;ve found an <a href="http://msmvps.com/jonathangreensted/archive/2004/12/07/22717.aspx">interesting article</a> explaining how one can do to find Word styles efficiently. The code was in C#, so the small burden was to convert C# code into Groovy, but that wasn&rsquo;t very complicated. I also put together all adjacent styled text so that I may build entire scripts (and not one line each time). And I was done!</p>
<p>The little program takes two arguments: the file name, and the style you wish to find in that document. You use simple and double quotes to surround the style name. I&rsquo;ve regrouped together all contiguous styles together. And what else&hellip; well, the other comments are in the source code! So let&rsquo;s see what the code looks like!</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.codehaus.groovy.scriptom.ActiveXProxy</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> style <span style="color:#666">=</span> <span style="color:#4070a0">&#34;&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> fileName <span style="color:#666">=</span> <span style="color:#4070a0">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>args<span style="color:#666">.</span><span style="color:#4070a0">length</span> <span style="color:#666">!=</span> <span style="color:#40a070">2</span><span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    println <span style="color:#4070a0">&#34;Usage: groovy extractStyle yourFile.doc yourStyle&#34;</span>
</span></span><span style="display:flex;"><span>    System<span style="color:#666">.</span><span style="color:#4070a0">exit</span><span style="color:#666">(</span><span style="color:#40a070">1</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">else</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    fileName <span style="color:#666">=</span> args<span style="color:#666">[</span><span style="color:#40a070">0</span><span style="color:#666">]</span>
</span></span><span style="display:flex;"><span>    style <span style="color:#666">=</span> removeQuotes<span style="color:#666">(</span>args<span style="color:#666">[</span><span style="color:#40a070">1</span><span style="color:#666">])</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// creating the Word application
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> word <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> ActiveXProxy<span style="color:#666">(</span><span style="color:#4070a0">&#34;Word.Application&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">try</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// opening your document
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    word<span style="color:#666">.</span><span style="color:#4070a0">Documents</span><span style="color:#666">.</span><span style="color:#4070a0">Open</span><span style="color:#666">(</span><span style="color:#007020;font-weight:bold">new</span> File<span style="color:#666">(</span>fileName<span style="color:#666">).</span><span style="color:#4070a0">canonicalPath</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// getting the content of the document
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#902000">def</span> doc <span style="color:#666">=</span> word<span style="color:#666">.</span><span style="color:#4070a0">ActiveDocument</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> range <span style="color:#666">=</span> doc<span style="color:#666">.</span><span style="color:#4070a0">Content</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// prepare the search of styled text
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    range<span style="color:#666">.</span><span style="color:#4070a0">Find</span><span style="color:#666">.</span><span style="color:#4070a0">ClearFormatting</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    range<span style="color:#666">.</span><span style="color:#4070a0">Find</span><span style="color:#666">.</span><span style="color:#4070a0">Style</span> <span style="color:#666">=</span> style
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> start<span style="color:#666">,</span> end <span style="color:#666">=</span> <span style="color:#40a070">0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> text <span style="color:#666">=</span> <span style="color:#4070a0">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#902000">def</span> found <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">true</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// while there is styled text to find...
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#007020;font-weight:bold">while</span> <span style="color:#666">(</span>found<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#60a0b0;font-style:italic">// execute the find command
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>
</span></span><span style="display:flex;"><span>        found <span style="color:#666">=</span> range<span style="color:#666">.</span><span style="color:#4070a0">Find</span><span style="color:#666">.</span><span style="color:#4070a0">Execute</span><span style="color:#666">().</span><span style="color:#4070a0">value</span>
</span></span><span style="display:flex;"><span>        start <span style="color:#666">=</span> range<span style="color:#666">.</span><span style="color:#4070a0">Start</span><span style="color:#666">.</span><span style="color:#4070a0">value</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>end <span style="color:#666">!=</span> start <span style="color:#666">&amp;&amp;</span> end <span style="color:#666">!=</span> <span style="color:#40a070">0</span><span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>            <span style="color:#60a0b0;font-style:italic">// seperate found occurencies with a separator
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>            println<span style="color:#666">(</span><span style="color:#4070a0">&#34;=&#34;</span> <span style="color:#666">*</span> <span style="color:#40a070">72</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>            println text
</span></span><span style="display:flex;"><span>            text <span style="color:#666">=</span> <span style="color:#4070a0">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>        <span style="color:#60a0b0;font-style:italic">// get the text content of the styled text found
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>        <span style="color:#60a0b0;font-style:italic">// without the last newline
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>        text <span style="color:#666">+=</span> removeNewLine<span style="color:#666">(</span>range<span style="color:#666">.</span><span style="color:#4070a0">Text</span><span style="color:#666">.</span><span style="color:#4070a0">value</span><span style="color:#666">)</span> <span style="color:#666">+</span> <span style="color:#4070a0">&#34;\n&#34;</span>
</span></span><span style="display:flex;"><span>        end <span style="color:#666">=</span> range<span style="color:#666">.</span><span style="color:#4070a0">End</span><span style="color:#666">.</span><span style="color:#4070a0">value</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">finally</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#60a0b0;font-style:italic">// quitting the Word application
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    <span style="color:#60a0b0;font-style:italic">// without saving the changes (wdDoNotSaveChanges = 0)
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    word<span style="color:#666">.</span><span style="color:#4070a0">Quit</span><span style="color:#666">(</span><span style="color:#40a070">0</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">/** Remove potential simple or double quotes around the name of the style to find */</span>
</span></span><span style="display:flex;"><span>String <span style="color:#06287e">removeQuotes</span><span style="color:#666">(</span>String s<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>s<span style="color:#666">.</span><span style="color:#4070a0">startsWith</span><span style="color:#666">(</span><span style="color:#4070a0">/&#39;/</span><span style="color:#666">)</span> <span style="color:#666">&amp;&amp;</span> s<span style="color:#666">.</span><span style="color:#4070a0">endsWith</span><span style="color:#666">(</span><span style="color:#4070a0">/&#39;/</span><span style="color:#666">)</span> <span style="color:#666">||</span> s<span style="color:#666">.</span><span style="color:#4070a0">startsWith</span><span style="color:#666">(</span><span style="color:#4070a0">/&#34;/</span><span style="color:#666">)</span> <span style="color:#666">&amp;&amp;</span> s<span style="color:#666">.</span><span style="color:#4070a0">endsWith</span><span style="color:#666">(</span><span style="color:#4070a0">/&#34;/</span><span style="color:#666">))</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">return</span> s<span style="color:#666">.</span><span style="color:#4070a0">substring</span><span style="color:#666">(</span><span style="color:#40a070">1</span><span style="color:#666">,</span> s<span style="color:#666">.</span><span style="color:#4070a0">length</span><span style="color:#666">()</span> <span style="color:#666">-</span> <span style="color:#40a070">1</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">else</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">return</span> s
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">/** Remove the last new line character which doesn&#39;t render well under DOS */</span>
</span></span><span style="display:flex;"><span>String <span style="color:#06287e">removeNewLine</span><span style="color:#666">(</span>String s<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">switch</span> <span style="color:#666">(</span>s<span style="color:#666">.</span><span style="color:#4070a0">length</span><span style="color:#666">())</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">case</span> <span style="color:#40a070">0</span><span style="color:#666">:</span> <span style="color:#007020;font-weight:bold">return</span> <span style="color:#4070a0">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">case</span> <span style="color:#40a070">1</span><span style="color:#666">:</span> <span style="color:#007020;font-weight:bold">return</span> <span style="color:#4070a0">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#007020;font-weight:bold">default</span><span style="color:#666">:</span> <span style="color:#007020;font-weight:bold">return</span> s<span style="color:#666">.</span><span style="color:#4070a0">substring</span><span style="color:#666">(</span><span style="color:#40a070">0</span><span style="color:#666">,</span> s<span style="color:#666">.</span><span style="color:#4070a0">length</span><span style="color:#666">()</span> <span style="color:#666">-</span> <span style="color:#40a070">1</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Castor tip: generating Java classes for XSD simple types</title><link>https://glaforge.dev/posts/2005/04/24/castor-tip-generating-java-classes-for-xsd-simple-types/</link><pubDate>Sun, 24 Apr 2005 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2005/04/24/castor-tip-generating-java-classes-for-xsd-simple-types/</guid><description>&lt;p>At work, I&amp;rsquo;m using &lt;a href="http://castor.codehaus.org/">Castor&lt;/a> XML to Java binding to marshall/unmarshall messages in my Web Services, inside a custom framework (Struts, OJB, JAXM, etc). I have defined my messages as XSD Schemas, and I&amp;rsquo;m using &lt;a href="http://maven.apache.org/reference/plugins/castor/">Castor&amp;rsquo;s Maven plugin&lt;/a> to auto-generate my Java classes at build time.&lt;/p>
&lt;p>All is good and well&amp;hellip; Hmm, almost! Castor&amp;rsquo;s SourceGenerator generates Java classes for complex types and elements, but not for simple types, and unfortunately, I badly needed to marshall those simple types as well. But fortunately, I&amp;rsquo;ve found a little trick to transform simple types to complex types, so that Castor can generate the associated Java classes, with its useful and handy marshall() methods.&lt;/p></description><content:encoded>
<![CDATA[<p>At work, I&rsquo;m using <a href="http://castor.codehaus.org/">Castor</a> XML to Java binding to marshall/unmarshall messages in my Web Services, inside a custom framework (Struts, OJB, JAXM, etc). I have defined my messages as XSD Schemas, and I&rsquo;m using <a href="http://maven.apache.org/reference/plugins/castor/">Castor&rsquo;s Maven plugin</a> to auto-generate my Java classes at build time.</p>
<p>All is good and well&hellip; Hmm, almost! Castor&rsquo;s SourceGenerator generates Java classes for complex types and elements, but not for simple types, and unfortunately, I badly needed to marshall those simple types as well. But fortunately, I&rsquo;ve found a little trick to transform simple types to complex types, so that Castor can generate the associated Java classes, with its useful and handy marshall() methods.</p>
<p>Let&rsquo;s take a little example. Say you have a simple type which is just a restriction upon strings:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;xs:simpletype</span> <span style="color:#4070a0">name=</span><span style="color:#4070a0">&#34;ApplicationType&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;xs:restriction</span> <span style="color:#4070a0">base=</span><span style="color:#4070a0">&#34;xs:string&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;xs:minlength</span> <span style="color:#4070a0">value=</span><span style="color:#4070a0">&#34;1&#34;</span><span style="color:#062873;font-weight:bold">&gt;&lt;/xs:minlength&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;xs:maxlength</span> <span style="color:#4070a0">value=</span><span style="color:#4070a0">&#34;32&#34;</span><span style="color:#062873;font-weight:bold">&gt;&lt;/xs:maxlength&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;/xs:restriction&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/xs:simpletype&gt;</span>
</span></span></code></pre></div><p>This type won&rsquo;t give you a Java class, thus you&rsquo;ll have to transform it into a complex type, and here is how you can do that:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;xs:simpletype</span> <span style="color:#4070a0">name=</span><span style="color:#4070a0">&#34;String32&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;xs:restriction</span> <span style="color:#4070a0">base=</span><span style="color:#4070a0">&#34;xs:string&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;xs:minlength</span> <span style="color:#4070a0">value=</span><span style="color:#4070a0">&#34;1&#34;</span><span style="color:#062873;font-weight:bold">&gt;&lt;/xs:minlength&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;xs:maxlength</span> <span style="color:#4070a0">value=</span><span style="color:#4070a0">&#34;32&#34;</span><span style="color:#062873;font-weight:bold">&gt;&lt;/xs:maxlength&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;/xs:restriction&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/xs:simpletype&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;xs:complextype</span> <span style="color:#4070a0">name=</span><span style="color:#4070a0">&#34;ApplicationType&#34;</span><span style="color:#062873;font-weight:bold">&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;xs:simplecontent&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#062873;font-weight:bold">&lt;xs:extension</span> <span style="color:#4070a0">base=</span><span style="color:#4070a0">&#34;ths:String32&#34;</span><span style="color:#062873;font-weight:bold">&gt;&lt;/xs:extension&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#062873;font-weight:bold">&lt;/xs:simplecontent&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#062873;font-weight:bold">&lt;/xs:complextype&gt;</span>
</span></span></code></pre></div><p>Though I&rsquo;m happy with that trick, I&rsquo;ve however filed a <a href="http://jira.codehaus.org/browse/CASTOR-1079">JIRA issue</a> in Castor&rsquo;s bugtracker, so that they can add a new option to allow us to generate Java classes for simple types too.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>RIFE rocks the pants off of Rails</title><link>https://glaforge.dev/posts/2005/03/18/rife-rocks-the-pants-off-of-rails/</link><pubDate>Fri, 18 Mar 2005 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2005/03/18/rife-rocks-the-pants-off-of-rails/</guid><description>&lt;p>Rails is kinda cool. Any framework that ease the development of CRUD-oriented webapps is a real time saver. There&amp;rsquo;s been a lot of well-deserved hype around Ruby on Rails lately. But I&amp;rsquo;m not sure that&amp;rsquo;s really that revolutionary. I&amp;rsquo;ve always thought that a good Java Web framework can do the same. Perhaps with a few more lines of code, since we all know that Java tends to be more verbose than some scripting languages. But overall, does it really matter? Especially since you tend to be much more productive in Java with a top-notch IDE like &lt;a href="http://www.jetbrains.com/">IntelliJ&lt;/a>!&lt;/p></description><content:encoded>
<![CDATA[<p>Rails is kinda cool. Any framework that ease the development of CRUD-oriented webapps is a real time saver. There&rsquo;s been a lot of well-deserved hype around Ruby on Rails lately. But I&rsquo;m not sure that&rsquo;s really that revolutionary. I&rsquo;ve always thought that a good Java Web framework can do the same. Perhaps with a few more lines of code, since we all know that Java tends to be more verbose than some scripting languages. But overall, does it really matter? Especially since you tend to be much more productive in Java with a top-notch IDE like <a href="http://www.jetbrains.com/">IntelliJ</a>!</p>
<p>One of the nice little apps written with Rails is <a href="http://www.tadalist.com/">Tada list</a>: well, basically, that&rsquo;s, er&hellip; a ToDo list. That&rsquo;s hard to find a more verbose definition! To show that one can do the same things in Java, with a nice framework, <a href="http://rifers.org/blogs/gbevin/">Geert Bevin</a> created a better clone of Tada: <a href="http://blablalist.com/">Blabla List</a>!</p>
<p>Geert found some severe security issues with Tada, and with just 50% lines of code more than the Rail&rsquo;s equivalent, he nonetheless implemented the same features, but he also fixed the security problems of Tada, and even added a <a href="http://rifers.org/wiki/display/BLA/REST+API">REST API</a>, reorderable lists and drag&rsquo;n drop support!</p>
<p>How did he do that in Java? He used <a href="http://openlaszlo.org/">Laszlo</a> for the cute flash user interface, and the wonderful <a href="https://rife.dev.java.net/">RIFE</a> framework which really rocks the pants off of Rails. Conclusion: A good Java MVC framework can do at least as good as Rails, if not better.</p>
<p>So now, you know what you have to do? Just <a href="http://blablalist.com/">sign up for free</a> and start keeping track of your todos online!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Re: How Groovy can get her groove back</title><link>https://glaforge.dev/posts/2005/01/31/re-how-groovy-can-get-her-groove-back/</link><pubDate>Mon, 31 Jan 2005 02:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2005/01/31/re-how-groovy-can-get-her-groove-back/</guid><description>&lt;p>I think &lt;a href="http://blog.taragana.com/index.php?p=146">Angsuman&lt;/a> diserve an answer.&lt;/p>
&lt;blockquote>
&lt;p>It looks like Groovy is in dire needs of a project manager.&lt;/p>&lt;/blockquote>
&lt;p>We&amp;rsquo;ve got one already. Of course, he&amp;rsquo;s been pretty busy with his &lt;a href="http://protique.com/">Active*&lt;/a> projects lately, but I think now he&amp;rsquo;s really back on the project. It&amp;rsquo;s often hard to cope with both a demanding job, and a demanding Open Source project. It&amp;rsquo;s tough to find time for both. Fortunately, he&amp;rsquo;s not alone on the Groovy project. While he was too busy, I took the interim and maintained Groovy and managed to deliver a few successful betas with a lot of bug fixes. But that&amp;rsquo;s just the tip of the iceberg. In November, with Jeremy, we organized a JSR meeting, and I&amp;rsquo;ve recruited a few new commiters to help us solidify our groovy-core base, so that it is able to receive the upcoming new parser. And the JSR work and discussions never stopped.&lt;/p></description><content:encoded>
<![CDATA[<p>I think <a href="http://blog.taragana.com/index.php?p=146">Angsuman</a> diserve an answer.</p>
<blockquote>
<p>It looks like Groovy is in dire needs of a project manager.</p></blockquote>
<p>We&rsquo;ve got one already. Of course, he&rsquo;s been pretty busy with his <a href="http://protique.com/">Active*</a> projects lately, but I think now he&rsquo;s really back on the project. It&rsquo;s often hard to cope with both a demanding job, and a demanding Open Source project. It&rsquo;s tough to find time for both. Fortunately, he&rsquo;s not alone on the Groovy project. While he was too busy, I took the interim and maintained Groovy and managed to deliver a few successful betas with a lot of bug fixes. But that&rsquo;s just the tip of the iceberg. In November, with Jeremy, we organized a JSR meeting, and I&rsquo;ve recruited a few new commiters to help us solidify our groovy-core base, so that it is able to receive the upcoming new parser. And the JSR work and discussions never stopped.</p>
<blockquote>
<p>One of the basic things we all learned along the way is that software is not about perfection. It is about delivering a good enough rock solid product on time. There is always version 2 to add more features. Nobody expects version 1 to be the ultimate solution.</p></blockquote>
<p>Often people mix OSS projects and IT projects. I don&rsquo;t think they are the same. The constraints aren&rsquo;t the same. For IT projects, you pay for something, so you oughta get back something you paid for in a reasonable timeframe, but in OSS-land, when the developers are just working in their spare time, besides their day job, how can you be so demanding? So far, I&rsquo;ve spent both time and money in Groovy, and I don&rsquo;t have anything back! Except, of course, the pleasure to have a few delighted users thank me for our awesome project.</p>
<p>What we want in Groovy 1.0 is to solidify the basic core features of Groovy, which have been there early in the project life. And contrary to what most persons think, we haven&rsquo;t added any significant new feature in the language for more than 6 months! We&rsquo;re finally standardizing the meaning of language constructs we never really took the time to implement correctly and propertly in a coherent manner (like the semantics of continue/break/return inside a closure).</p>
<p>The scope of the project is the same as it was several months ago. In our implementation there&rsquo;s just room for extensibility. Like supporting Java 5 annotations with the help of the Java 5 grammar we&rsquo;ve based the new parser upon, or adding a new operator (.@) to help us diminsh the level of ambiguity in the scoping rules.</p>
<blockquote>
<p>In today world people have come to expect stable beta (remember JDOM betas?), unlike what James comments in Spille blog. If the build is to be highly unstable then it should probably be in alpha stages. Beta stages are all about refinement and limiting feature set to get a release.</p></blockquote>
<p>We made mistakes in our numbering scheme, that&rsquo;s granted. We should have started with 0.1, 0.2, 0.x, then get to a 1.0-beta-x, and finally perhaps some 1.0-RC-x, before delivering groovy-final-1.0. We&rsquo;re trying to fix that by changing slightly our tags: there will be a new beta soon (beta-10 within two weeks), and the few next milestones will be tagged jsr-x and will include the new parser. And after that, we should probably release Groovy 1.0 before the end of Q2. I won&rsquo;t give you a firm and definitive target date, it&rsquo;s simply impossible, because we&rsquo;re not an IT project, and we can&rsquo;t know what could happen to any of us (a new demanding job, a baby, whatever&hellip;)</p>
<p>In IT-land, there are four variable you can play with: scope, cost, time and quality:</p>
<ul>
<li>The scope hasn&rsquo;t changed for months, and we want to keep the features which had already been there since day one, otherwise, why use Groovy if Groovy has just got the same features as BeanShell or Jython?</li>
<li>The cost is just our spare time and limited number of developers, and we can&rsquo;t really tweak that variable, since it depends a lot on our day jobs, family, and personal constraints, (even though we recruited a few new commiters who help us solidify the base)</li>
<li>The quality mustn&rsquo;t be compromised,</li>
<li>The time variable is the only one we can play with to improve the quality.</li>
</ul>
<blockquote>
<p>Even Open Source projects are not immune to this paradigm, specially for a project which have garnered so much interest.</p></blockquote>
<p>We&rsquo;ve garnered a lot of interest. An incredible amount. And I&rsquo;m glad about that. I hope we won&rsquo;t dissapoint anyone, or at least that just a few will be upset by our decisions. I wonder whether other OSS projects have had that much pressure on developers&rsquo; shoulders. Could you name even one? Expectations are very high regarding Groovy.</p>
<blockquote>
<p>I think the theme which comes out of these discussions is that Groovy project leads needs to fix a reasonable date of release 1 and stick to it making compromises along the way. And it needs help.</p></blockquote>
<p>We&rsquo;re going to deliver Groovy with the original scope James envisionned. Not much more, not much less. We&rsquo;re focusing on the core functionnalities. We&rsquo;ve already wired the new parser in CVS Head within just a few days of work, and the progress was amazingly quick to show up. We&rsquo;re going to ship it perhaps in the next beta (beta-10) if we delay the beta for one or two weeks, or we&rsquo;ll ship it at the end of February. We need to settle on the best date for a first warm-up of the parser.</p>
<p>We&rsquo;ve also started sketching our <a href="http://groovy.codehaus.org/Roadmap">roadmap</a> more accurately to offer more visibility to our beloved users and community.</p>
<blockquote>
<p>Guillaume commented on the pressure on the Groovy’s developers. That is true. However for an Open Source project which has garnered so much interest, press and enthusiasm all around it is to be expected. That the price for fame :)</p></blockquote>
<p>Oh really, am I becoming famous? :-) Ah, no&hellip; only the project! Well, I&rsquo;ve never worked on Groovy to become famous anyway. I wanted the right tool for the job. I got involved into Groovy because I needed a scripting language for the JVM, to allow us to write some dynamic logic, business rules, modifyable at runtime, without the hassle of re-deploying software everytime, just because some business logic changed in the course of time. Groovy&rsquo;s going to bring agility in your projects, and will be a real time saver for your shell scripting needs, wiring platform specific capabilities (like with <a href="http://groovy.codehaus.org/COM+Scripting">Scriptom</a>) and the vast wealth of available java components and libraries.</p>
<p>Groovy is the right tool for agility business.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy's dead, long live Groovy!</title><link>https://glaforge.dev/posts/2005/01/31/groovy-s-dead-long-live-groovy/</link><pubDate>Mon, 31 Jan 2005 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2005/01/31/groovy-s-dead-long-live-groovy/</guid><description>&lt;p>Incredible, I&amp;rsquo;ve just learnt that &lt;a href="http://www.pyrasun.com/mike/mt/archives/2005/01/28/19.10.21/index.html">Groovy was dead&lt;/a>, according to Mike! So I understand that&amp;rsquo;s why Cédric would like to know the &lt;a href="http://beust.com/weblog/archives/000235.html">final date for the burial&lt;/a>.&lt;/p>
&lt;p>It&amp;rsquo;s interesting to see how much pressure the &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> project has on its little shoulders. So far, to my knowledge, I don&amp;rsquo;t really see any other OSS project for which the users and the blogosphere had so many expectations. Well, back in time, 6 months ago, yeah, we could&amp;rsquo;ve said Groovy was somewhat a zombie, but I don&amp;rsquo;t think that&amp;rsquo;s true anymore, with the latest months activity. We&amp;rsquo;re certainly not good at public relations these days, we&amp;rsquo;ve got some efforts to make on that front.&lt;/p></description><content:encoded>
<![CDATA[<p>Incredible, I&rsquo;ve just learnt that <a href="http://www.pyrasun.com/mike/mt/archives/2005/01/28/19.10.21/index.html">Groovy was dead</a>, according to Mike! So I understand that&rsquo;s why Cédric would like to know the <a href="http://beust.com/weblog/archives/000235.html">final date for the burial</a>.</p>
<p>It&rsquo;s interesting to see how much pressure the <a href="http://groovy.codehaus.org/">Groovy</a> project has on its little shoulders. So far, to my knowledge, I don&rsquo;t really see any other OSS project for which the users and the blogosphere had so many expectations. Well, back in time, 6 months ago, yeah, we could&rsquo;ve said Groovy was somewhat a zombie, but I don&rsquo;t think that&rsquo;s true anymore, with the latest months activity. We&rsquo;re certainly not good at public relations these days, we&rsquo;ve got some efforts to make on that front.</p>
<p>With the team, we&rsquo;ve discussed a roadmap for the next few months, but really, it&rsquo;s hard to give a definitive date. We&rsquo;re going to release beta-10 within two weeks, then, a month later, we&rsquo;ll be able to play with jsr-1 (formerly known as beta-11) which will use the new parser if you activate the right flag. Then the next jsr-x versions will use only that new parser by default. I think June could be a plausible target date for Groovy 1.0 final. We&rsquo;ll see if that&rsquo;s possible with our limited resources. I think we should publish a Roadmap page on our website, so that our users know what to expect from us.</p>
<p>John Rose wrote an initial grammar with Antlr as a diff with a Java 1.3 grammar, and <a href="http://javanicus.com/blog2/">Jeremy Rayner</a> ported it to a Java 1.5 grammar. Last week, <a href="http://radio.weblogs.com/0112098/">James Strachan</a> worked on wiring this new grammar and parser into the Groovy runtime, and the progress was pretty fast. And with confidence, I can already tell you that within a month and a half, you&rsquo;ll be able to play with the &ldquo;New Groovy&rdquo;. Currently, there are still a few rough edges of course, and there&rsquo;s still heaps of work to do, especially on the builders, on fixing the semantics of the method invocation and the scoping rules, on implementing the closures&rsquo; logic coherently, but that&rsquo;s doable.</p>
<p>So really, is Groovy dead? I don&rsquo;t think so.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy code-completion in IntelliJ</title><link>https://glaforge.dev/posts/2005/01/26/groovy-code-completion-in-intellij/</link><pubDate>Wed, 26 Jan 2005 02:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2005/01/26/groovy-code-completion-in-intellij/</guid><description>&lt;p>JetBrains improved the custom file type support in IntelliJ, in the Irida EAPs. If you have defined your Groovy syntax file correctly, you can have syntax highlighting, brace highlighting, and even&amp;hellip; code-completion!&lt;/p>
&lt;p>In my &lt;a href="http://glaforge.free.fr/groovy/Groovy.xml">Groovy syntax file&lt;/a> (which works in &lt;a href="http://www.intellij.net/eap/products/idea/download.jsp">Irida #3185&lt;/a>), I defined two sets of keywords: one for the keywords of the language, and the other one for the &lt;a href="http://groovy.codehaus.org/groovy-jdk.html">Groovy methods&lt;/a>, like each(), findAll(), etc. And guess what? Simply hit CTRL-Space as usual, and presto, you can complete your code.&lt;/p></description><content:encoded>
<![CDATA[<p>JetBrains improved the custom file type support in IntelliJ, in the Irida EAPs. If you have defined your Groovy syntax file correctly, you can have syntax highlighting, brace highlighting, and even&hellip; code-completion!</p>
<p>In my <a href="http://glaforge.free.fr/groovy/Groovy.xml">Groovy syntax file</a> (which works in <a href="http://www.intellij.net/eap/products/idea/download.jsp">Irida #3185</a>), I defined two sets of keywords: one for the keywords of the language, and the other one for the <a href="http://groovy.codehaus.org/groovy-jdk.html">Groovy methods</a>, like each(), findAll(), etc. And guess what? Simply hit CTRL-Space as usual, and presto, you can complete your code.</p>
<p>Here is a little picture of what it looks like:</p>
<p><figure>
  <a href="#img-65aa08ab888ef7e8f2f8867e61656083">
    <img src="/img/misc/groovyIntelliJ.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-65aa08ab888ef7e8f2f8867e61656083">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/groovyIntelliJ.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>The completion works only for keywords and the <a href="http://groovy.codehaus.org/groovy-jdk.html">Groovy methods</a>, but until someone develops a Groovy plugin for IntelliJ IDEA, this basic completion rocks!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Auto-completion in a DOS console</title><link>https://glaforge.dev/posts/2005/01/26/auto-completion-in-a-dos-console/</link><pubDate>Wed, 26 Jan 2005 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2005/01/26/auto-completion-in-a-dos-console/</guid><description>&lt;p>When I&amp;rsquo;m on an old PC, what frustrates me a lot is when I can&amp;rsquo;t auto-complete file names or paths when I&amp;rsquo;m in a DOS Console. If your computer (an old Win2K box like I have at work) is not configured for completion, it&amp;rsquo;s easy to re-enable it by modifying two keys in the registry:&lt;/p>
&lt;ul>
&lt;li>&lt;code>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor\CompletionChar&lt;/code>&lt;/li>
&lt;li>&lt;code>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor\PathCompletionChar&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>To both keys, assign an hexadecimal value representing the key used for completing statements. I&amp;rsquo;m quite used to the tab key for that purpose, so I set the hexadecimal value to 0x09.&lt;/p></description><content:encoded>
<![CDATA[<p>When I&rsquo;m on an old PC, what frustrates me a lot is when I can&rsquo;t auto-complete file names or paths when I&rsquo;m in a DOS Console. If your computer (an old Win2K box like I have at work) is not configured for completion, it&rsquo;s easy to re-enable it by modifying two keys in the registry:</p>
<ul>
<li><code>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor\CompletionChar</code></li>
<li><code>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor\PathCompletionChar</code></li>
</ul>
<p>To both keys, assign an hexadecimal value representing the key used for completing statements. I&rsquo;m quite used to the tab key for that purpose, so I set the hexadecimal value to 0x09.</p>
<p>Of course, I couldn&rsquo;t resist to use <a href="http://groovy.codehaus.org/">Groovy</a> and <a href="http://glaforge.free.fr/weblog/groovy.codehaus.org/COM+Scripting">Scriptom</a> (Windows ActiveX/COM bridge for Groovy I developed recently) to automate the process, here is how:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.codehaus.groovy.scriptom.ActiveXProxy</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>wshell <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> ActiveXProxy<span style="color:#666">(</span><span style="color:#4070a0">&#34;WScript.Shell&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>wshell<span style="color:#666">.</span><span style="color:#4070a0">RegWrite</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Command Processor\\CompletionChar&#34;</span><span style="color:#666">,</span> <span style="color:#40a070">9</span><span style="color:#666">,</span> <span style="color:#4070a0">&#34;REG_DWORD&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>wshell<span style="color:#666">.</span><span style="color:#4070a0">RegWrite</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Command Processor\\PathCompletionChar&#34;</span><span style="color:#666">,</span> <span style="color:#40a070">9</span><span style="color:#666">,</span> <span style="color:#4070a0">&#34;REG_DWORD&#34;</span><span style="color:#666">)</span>
</span></span></code></pre></div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Converting a Word document to HTML</title><link>https://glaforge.dev/posts/2005/01/24/converting-a-word-document-to-html/</link><pubDate>Mon, 24 Jan 2005 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2005/01/24/converting-a-word-document-to-html/</guid><description>&lt;p>If you&amp;rsquo;re under Windows, and that you need to do some shell scripting with ActiveX/COM components, Scriptom will certainly help you. Today, my boss just asked me if we could use &lt;a href="http://groovy.codehaus.org/COM%20Scripting">Scriptom&lt;/a> to convert a Word document into an HTML equivalent. And I decided to see if that was possible. To my delight, my little Scriptom module, backed by &lt;a href="http://danadler.com/jacob/">Jacob&lt;/a>, helped me solve this integration problem with only 6 lines of Groovy code!&lt;/p></description><content:encoded>
<![CDATA[<p>If you&rsquo;re under Windows, and that you need to do some shell scripting with ActiveX/COM components, Scriptom will certainly help you. Today, my boss just asked me if we could use <a href="http://groovy.codehaus.org/COM%20Scripting">Scriptom</a> to convert a Word document into an HTML equivalent. And I decided to see if that was possible. To my delight, my little Scriptom module, backed by <a href="http://danadler.com/jacob/">Jacob</a>, helped me solve this integration problem with only 6 lines of Groovy code!</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.codehaus.groovy.scriptom.ActiveXProxy</span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">java.io.File</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>word <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> ActiveXProxy<span style="color:#666">(</span><span style="color:#4070a0">&#34;Word.Application&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>word<span style="color:#666">.</span><span style="color:#4070a0">Documents</span><span style="color:#666">.</span><span style="color:#4070a0">Open</span><span style="color:#666">(</span><span style="color:#007020;font-weight:bold">new</span> File<span style="color:#666">(</span>args<span style="color:#666">[</span><span style="color:#40a070">0</span><span style="color:#666">]).</span><span style="color:#4070a0">canonicalPath</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>word<span style="color:#666">.</span><span style="color:#4070a0">ActiveDocument</span><span style="color:#666">.</span><span style="color:#4070a0">SaveAs</span><span style="color:#666">(</span><span style="color:#007020;font-weight:bold">new</span> File<span style="color:#666">(</span>args<span style="color:#666">[</span><span style="color:#40a070">1</span><span style="color:#666">]).</span><span style="color:#4070a0">canonicalPath</span><span style="color:#666">,</span> <span style="color:#40a070">8</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>word<span style="color:#666">.</span><span style="color:#4070a0">Quit</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>Now, I just need to launch:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>groovy word2html.groovy specification.doc specification.html
</span></span></code></pre></div><p>And I&rsquo;ve got a nice Word to HTML converter! Well&hellip; I know, not that nice. First of all, it&rsquo;s a Windows-only solution, but that fits my requirements regarding the platform I&rsquo;m running on, but the other negative aspect is that the generated HTML is really, really ugly. I really wonder why Microsoft can&rsquo;t do a cleaner output. For the moment, I&rsquo;m happy with that solution.</p>
<p>You probably noticed the magic number 8. It&rsquo;s the HTML format option. The available formats are:</p>
<ul>
<li>0: <code>wdFormatDocument</code> (no conversion)</li>
<li>1: <code>wdFormatTemplate</code></li>
<li>2: <code>wdFormatText</code></li>
<li>3: <code>wdFormatTextLineBreaks</code></li>
<li>4: <code>wdFormatDOSText</code></li>
<li>5: <code>wdFormatDOSTextLineBreaks</code></li>
<li>6: <code>wdFormatRTF</code></li>
<li>7: <code>wdFormatUnicodeText</code></li>
<li>8: <code>wdFormatHTML</code></li>
</ul>
<p>I haven&rsquo;t yet figured out how to be able to use constants directly in Groovy. I&rsquo;ll have to make Scriptom grok M$&rsquo;s constants.</p>
<p>The example I&rsquo;ve talked about has been tested with groovy-beta-9, Word 2000 and my additional Scriptom module for Groovy (don&rsquo;t forget to <a href="http://groovy.codehaus.org/COM%20Scripting">install</a> it if you want to try that sample).</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Noeuds de cravate</title><link>https://glaforge.dev/posts/2005/01/16/noeuds-de-cravate/</link><pubDate>Sun, 16 Jan 2005 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2005/01/16/noeuds-de-cravate/</guid><description>&lt;p>Qui dit nouveau boulot, dit être bien habillé. Il faut être classe et élégant. Evidemment, il n&amp;rsquo;y a pas que le premier jour qu&amp;rsquo;il faut l&amp;rsquo;être ! Mais c&amp;rsquo;est l&amp;rsquo;occasion de réviser ses classiques, et je pense particulièrement au noeud de cravate.&lt;/p>
&lt;p>J&amp;rsquo;ai une ribambelle de cravates dont les noeuds sont déjà faits. Je n&amp;rsquo;ai pas besoin de les refaire. Sauf si vraiment je ne suis pas satisfait du résultat. Mais en l&amp;rsquo;occurence, pour un nouveau costume, avec une nouvelle chemise, j&amp;rsquo;ai aussi opté pour une nouvelle cravate.&lt;/p></description><content:encoded>
<![CDATA[<p>Qui dit nouveau boulot, dit être bien habillé. Il faut être classe et élégant. Evidemment, il n&rsquo;y a pas que le premier jour qu&rsquo;il faut l&rsquo;être ! Mais c&rsquo;est l&rsquo;occasion de réviser ses classiques, et je pense particulièrement au noeud de cravate.</p>
<p>J&rsquo;ai une ribambelle de cravates dont les noeuds sont déjà faits. Je n&rsquo;ai pas besoin de les refaire. Sauf si vraiment je ne suis pas satisfait du résultat. Mais en l&rsquo;occurence, pour un nouveau costume, avec une nouvelle chemise, j&rsquo;ai aussi opté pour une nouvelle cravate.</p>
<p>Les <a href="http://membres.lycos.fr/vernaye/cravates/noeuds.html">noeuds de cravate</a> ne sont généralement pas très compliqués à faire. Tout du moins, le grand classique du genre : le <a href="http://membres.lycos.fr/vernaye/cravates/simple.html">noeud simple</a> (aussi appelé four-in-hand à l&rsquo;anglaise) est par définition simple à réaliser.</p>
<p>Le noeud simple est le plus couramment utilisé. Il est facile à mémoriser. Il convient bien à tout type de cravate : fine ou épaisse. Par contre, son principal défaut est qu&rsquo;il semble toujours être légèrement de travers. L&rsquo;effet est peut-être moins flagrant lorsque la cravate est très serrée ou si elle est suffisamment large. Mais généralement, on préfèrera des noeuds plus complexes qui ont une meilleure tenue et sont bien droits. Malheureusement, les noeuds plus compliqués conviennent moins bien aux cravates courtes et épaisses, et surtout, ils sont plus difficiles à mémoriser.</p>
<p>Les <a href="http://www.jules.fr/guide/noeuds.asp">noeuds les plus courants</a> sont :</p>
<ul>
<li>le noeud simple,</li>
<li>le <a href="http://membres.lycos.fr/vernaye/cravates/p_albert.html">noeud double</a>(dérivé du noeud simple),</li>
<li>le <a href="http://membres.lycos.fr/vernaye/cravates/windsor.html">Windsor</a>, et</li>
<li>le <a href="http://membres.lycos.fr/vernaye/cravates/d_windsor.html">demi-Windsor</a>.</li>
</ul>
<p>Le noeud double (aussi appelé double-simple ou Prince Albert) est un sans doute un peu moins oblique que son petit frère, le noeud simple, et il se prête bien aux cravates suffisamment longues et pas trop épaisses.</p>
<p>Les noeuds de la famille des Windsor sont eux plus stables et non pas ce léger effet de travers qu&rsquo;ont les noeuds simples. Par contre, comme tout noeud plus complexe, je trouve qu&rsquo;il va mieux aux cravates fines, et cela permet de donner beaucoup plus d&rsquo;ampleur et de volume au noeud. Un petit effet classe non-négligeable.</p>
<p>Mais il n&rsquo;y a pas que les noeuds simples et les Windsor, il y en a bien d&rsquo;autres ! Je ne parlerai pas du <a href="http://membres.lycos.fr/vernaye/cravates/bow.html">noeud papillon</a>, mais je voudrais vous en lister quelques autres :</p>
<ul>
<li>le <a href="http://membres.lycos.fr/vernaye/cravates/plattsburgh.html">Plattsburgh</a> (Dovorian),</li>
<li>le <a href="http://membres.lycos.fr/vernaye/cravates/balthus.html">Balthus</a> (dérivé du Plattsburgh),</li>
<li>le Cavendish,</li>
<li>le <a href="http://membres.lycos.fr/vernaye/cravates/grantchester.html">Grandchester</a>,</li>
<li>le <a href="http://membres.lycos.fr/vernaye/cravates/hanovre.html">Hanovre</a>,</li>
<li>l&rsquo;Oriental,</li>
<li>le <a href="http://membres.lycos.fr/vernaye/cravates/kelvin.html">Kelvin</a> (dérivé de l&rsquo;Oriental),</li>
<li>le <a href="http://membres.lycos.fr/vernaye/cravates/nicky.html">Nicky</a>,</li>
<li>l&rsquo;Onassis,</li>
<li>le petit noeud,</li>
<li>le Pratt,</li>
<li>le Saint-Andrews,</li>
<li>le Victoria.</li>
</ul>
<p>Ce sont des noeuds standards, par opposition au noeufs fantaisistes qui ne risqueraient pas de passer inaperçu :</p>
<ul>
<li>l&rsquo;<a href="http://membres.lycos.fr/vernaye/cravates/ascot.html">Ascot</a>,</li>
<li>le <a href="http://membres.lycos.fr/vernaye/cravates/croise.html">croisé</a> (Christensen ou Prince Hugues).</li>
</ul>
<p>Tout ce que je viens de vous raconter est expliqué en détail sur cet <a href="http://membres.lycos.fr/vernaye/cravates/noeuds.html">excellent site</a> avec des schémas pour pouvoir reproduire chacun de ces noeuds. A mettre dans ses favoris.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A bit of Groovy history...</title><link>https://glaforge.dev/posts/2005/01/05/a-bit-of-groovy-history/</link><pubDate>Wed, 05 Jan 2005 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2005/01/05/a-bit-of-groovy-history/</guid><description>&lt;p>First of all, I wish a Happy New Year to all my readers! The first days of a new year are often days of recollection, where we look back at the previous year. What happened? Was it a good year? Did we succeed in our endeavours? Any downside or missed target? Etc&amp;hellip;&lt;/p>
&lt;p>Today, I want to give you some insight in the Groovy history, as it was written last year, in 2004. It&amp;rsquo;s been a pretty busy year, in fact. Till the JSR got started, we were working like mad on Groovy, adding feature after feature, certainly not taking care enough of bugs, but still, that was a good time. Then came the JSR process, which somehow stopped all our efforts, because of uncertainty, and other considerations, like fear of not doing well with regards to that JSR process. Unfortunately, Groovy&amp;rsquo;s development almost stopped. Some of the core developers even left, because of the inactivity, and frustration of not being allowed to change things, or because of the lack of management in the project since our leader started working on other projects, taking all of his time.&lt;/p></description><content:encoded>
<![CDATA[<p>First of all, I wish a Happy New Year to all my readers! The first days of a new year are often days of recollection, where we look back at the previous year. What happened? Was it a good year? Did we succeed in our endeavours? Any downside or missed target? Etc&hellip;</p>
<p>Today, I want to give you some insight in the Groovy history, as it was written last year, in 2004. It&rsquo;s been a pretty busy year, in fact. Till the JSR got started, we were working like mad on Groovy, adding feature after feature, certainly not taking care enough of bugs, but still, that was a good time. Then came the JSR process, which somehow stopped all our efforts, because of uncertainty, and other considerations, like fear of not doing well with regards to that JSR process. Unfortunately, Groovy&rsquo;s development almost stopped. Some of the core developers even left, because of the inactivity, and frustration of not being allowed to change things, or because of the lack of management in the project since our leader started working on other projects, taking all of his time.</p>
<p>But around october, I was really fed up with that state of things. I really wanted things to change! Groovy was somehow dying, if no action were taken. That&rsquo;s why I started a big effort on making the Groovy ship move again. With my friend, <a href="http://javanicus.com/blog2/">Jeremy Rayner</a>, we managed to wake up everybody, to organize a JSR meeting in November, and I spent a huge amount of time fixing bugs in the current implementation to make a <a href="http://article.gmane.org/gmane.comp.lang.groovy.devel/2091">beta-8 release</a> and to help our users as much as I could on the forums. I &ldquo;hired&rdquo; also some new developers who helped me in the process. Thanks a lot to them all (Russel Winder, Jochen Theodorou, Dierk Koenig, and Scott Stirling). The JSR is also progressing thanks to the efforts of Jeremy Rayner, and John Rose with his first cut at the new Groovy grammar. For this new year, Jeremy gave us a pretty good summary and <a href="http://article.gmane.org/gmane.comp.lang.groovy.devel/2159">overview of the work in the JSR process</a>.</p>
<p>We&rsquo;ve been <a href="http://www.jroller.com/page/fate/?anchor=the_groovy_sinking_ship">biled</a>, we&rsquo;re certainly going to receive some other similar criticism. Hani&rsquo;s got pretty good points, and we&rsquo;re trying to address those concerns and the concerns of our users as much as we can. I hope Groovy will mature even more in 2005, and I think we should have a fairly good and standardized implementation.</p>
<p>The primary reason for that blog entry was to guide you through the history behing the JSR process. That&rsquo;s Richard Manson-Haefeld who&rsquo;s mostly responsible for that, and he just blogged about it. He guides us through the <a href="http://rmh.blogs.com/weblog/2005/01/groovy_a_bit_of.html">history of the Groovy JSR</a>.</p>
<p>2005 should be a Groovy year! So, Groovy new year to everybody!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Scripting ActiveX/COM components with Groovy</title><link>https://glaforge.dev/posts/2004/12/30/scripting-activex-com-components-with-groovy/</link><pubDate>Thu, 30 Dec 2004 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2004/12/30/scripting-activex-com-components-with-groovy/</guid><description>&lt;p>During my three weeks break before my next job, I decided I&amp;rsquo;d work on two things: first, I&amp;rsquo;ll play with Groovy a little more (playing != fixing bugs like mad like those last two months to get beta-8 out in the wild), and I&amp;rsquo;ll work on my &amp;ldquo;Learning Groovy&amp;rdquo; book for O&amp;rsquo;Reilly.&lt;/p>
&lt;p>In this blog post, I&amp;rsquo;ll talk about my last two days playing with Groovy and&amp;hellip; &lt;a href="http://danadler.com/jacob/">Jacob&lt;/a>, a Java COM Bridge library developed by &lt;a href="http://danadler.com/">Dan Adler&lt;/a>, to interact with Windows components. So, of course, it&amp;rsquo;s Windows-only.&lt;/p></description><content:encoded>
<![CDATA[<p>During my three weeks break before my next job, I decided I&rsquo;d work on two things: first, I&rsquo;ll play with Groovy a little more (playing != fixing bugs like mad like those last two months to get beta-8 out in the wild), and I&rsquo;ll work on my &ldquo;Learning Groovy&rdquo; book for O&rsquo;Reilly.</p>
<p>In this blog post, I&rsquo;ll talk about my last two days playing with Groovy and&hellip; <a href="http://danadler.com/jacob/">Jacob</a>, a Java COM Bridge library developed by <a href="http://danadler.com/">Dan Adler</a>, to interact with Windows components. So, of course, it&rsquo;s Windows-only.</p>
<p>I created <a href="http://groovy.codehaus.org/COM+Scripting">Scriptom</a>: a new module (optional Groovy component) that allows you to script any COM or ActiveX component with Groovy. That is you can control applications like Internet Explorer or Excel with groovy scripts.</p>
<p>Groovy&rsquo;s metaprogramming facilities make it a language of choice for doing some funny stuff like writing transparent proxies, mock objects, or other wacky stuff, like our builders (Ant builder, Swing builder, or Markup builder). You can intercept calls to non-existant methods and make them do something for you, like creating a new tag, or an Ant task, etc.</p>
<p>Being able to write some meta-object was the key to this problem of interoperability between Groovy and COM components. I wrote a pair of classes implementing <a href="http://cvs.codehaus.org/viewrep/~raw,r=1.3/groovy/groovy/groovy-core/src/main/groovy/lang/GroovyObjectSupport.java">GroovyObjectSupport</a>, to make a kind of proxy around some of the Jacob internals. So any call to a method or property of my proxy would delegate the call to the foreign component. But let&rsquo;s stop chatting about the inner workings, and let&rsquo;s see some examples of what we can do!</p>
<p>First of all, if you wanna play with Scriptom, the best thing is to go <a href="http://groovy.codehaus.org/COM+Scripting">read the documentation</a>! Everything is explained: how to get it, how to install it, and some samples are there as well for good measure. Scriptom&rsquo;s been tested on groovy-1.0-beta-8 and Windows XP Home Edition.</p>
<p>As everything is explained in the documentation, I won&rsquo;t show you all the examples, but let&rsquo;s just look at the one for scripting Internet Explorer:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">org.codehaus.groovy.scriptom.ActiveXProxy</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// instanciate Internet Explorer
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> explorer <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> ActiveXProxy<span style="color:#666">(</span><span style="color:#4070a0">&#34;InternetExplorer.Application&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// set its properties
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>explorer<span style="color:#666">.</span><span style="color:#4070a0">Visible</span> <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">true</span>
</span></span><span style="display:flex;"><span>explorer<span style="color:#666">.</span><span style="color:#4070a0">AddressBar</span> <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">true</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// navigate to a site by calling the Navigate() method
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>explorer<span style="color:#666">.</span><span style="color:#4070a0">Navigate</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;http://glaforge.free.fr/weblog&#34;</span><span style="color:#666">)</span>
</span></span></code></pre></div><p>You&rsquo;re using Internet Explorer as if it were a standard Java or Groovy object! Pretty neat, uh?</p>
<p>There are several other examples in the documentation and in the sources. If ever an application provides some ActiveX or COM components, you can script it with Groovy. For instance, you could suck up some data from a database with Groovy SQL, and write an Excel spreadsheet with formulas to manipulate the data. Or you can script Media Player to make it play all the sounds in a given directory that you&rsquo;d have chosen with a native Windows folder chooser, etc. Many possibilities that all the VBScript gurus and Windows Scripting Host admins will like. Moreover, you can interact with Java and Groovy. So you have the best of both worlds: the JVM world and its wealth of libraries, and the native Windows world.</p>
<p>I&rsquo;ve chosen Jacob over JCom because it seems to be able to allow us to wire our own event handlers, which is not possible with JCom, as far as I know. So my next step will be to provide some funky way of writing our own event handlers in Groovy, probably with some closures&hellip; Stay tuned!</p>
<p>That&rsquo;s fun, and I hope you&rsquo;ll enjoy playing with Scriptom! That&rsquo;s my late Christmas gift to you!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy support in IntelliJ</title><link>https://glaforge.dev/posts/2004/11/29/groovy-support-in-intellij/</link><pubDate>Mon, 29 Nov 2004 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2004/11/29/groovy-support-in-intellij/</guid><description>&lt;p>Kjetil JD submitted a &lt;a href="http://www.intellij.net/tracker/idea/viewSCR?publicId=40320">feature request&lt;/a> on JetBrains&amp;rsquo; tracker for adding Groovy support to IntelliJ:&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Integrated Groovy-support&lt;/strong>&lt;br />
Integrated support for Java bytecode-based scripting languages should be better integrated in IntelliJ. Groovy is the only Java bytecode scripting language that has a backing JSR and should be the first to be implemented with proper support like Java and XML has.&lt;/p>&lt;/blockquote>
&lt;p>For the moment, the Groovy IntelliJ plugin is somewhat stalled, we didn&amp;rsquo;t make any progress on that front, so a little help from our JetBrains friends would be most welcome.&lt;/p></description><content:encoded>
<![CDATA[<p>Kjetil JD submitted a <a href="http://www.intellij.net/tracker/idea/viewSCR?publicId=40320">feature request</a> on JetBrains&rsquo; tracker for adding Groovy support to IntelliJ:</p>
<blockquote>
<p><strong>Integrated Groovy-support</strong><br />
Integrated support for Java bytecode-based scripting languages should be better integrated in IntelliJ. Groovy is the only Java bytecode scripting language that has a backing JSR and should be the first to be implemented with proper support like Java and XML has.</p></blockquote>
<p>For the moment, the Groovy IntelliJ plugin is somewhat stalled, we didn&rsquo;t make any progress on that front, so a little help from our JetBrains friends would be most welcome.</p>
<p>So far, at the time of this writing, it&rsquo;s interesting to note that the feature request received 937 votes, and is already ranked at the 12th position of the most voted features! Thus, if you&rsquo;re interested in seeing Groovy support in your <a href="http://www.jetbrains.com/idea">favourite IDE</a>, please cast your votes on that <a href="http://www.intellij.net/tracker/idea/viewSCR?publicId=40320">feature request</a>!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy Conference 1</title><link>https://glaforge.dev/posts/2004/11/28/groovy-conference-1/</link><pubDate>Sun, 28 Nov 2004 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2004/11/28/groovy-conference-1/</guid><description>&lt;p>So far, I didn&amp;rsquo;t blog about our first ever Groovy Conference we held on November 11th and November 12th. But at least, here are some pictures taken there. I&amp;rsquo;ve created a &lt;a href="http://www.flickr.com/">Flickr&lt;/a> account and put &lt;a href="http://www.flickr.com/photos/72768689@N00/tags/groovy/">my pictures&lt;/a> son it.&lt;/p>
&lt;p>Jeremy Rayner &lt;a href="http://web1.2020media.com/j/jez/javanicuscom/blog2/items/165-index.html">blogged&lt;/a> about the conference, and also took &lt;a href="http://www.flickr.com/photos/jez/tags/groovy/">some pictures&lt;/a>, and wrote down a &lt;a href="http://javanicus.com/blog2/items/166-index.html">few notes&lt;/a> about the conference.&lt;/p></description><content:encoded>
<![CDATA[<p>So far, I didn&rsquo;t blog about our first ever Groovy Conference we held on November 11th and November 12th. But at least, here are some pictures taken there. I&rsquo;ve created a <a href="http://www.flickr.com/">Flickr</a> account and put <a href="http://www.flickr.com/photos/72768689@N00/tags/groovy/">my pictures</a> son it.</p>
<p>Jeremy Rayner <a href="http://web1.2020media.com/j/jez/javanicuscom/blog2/items/165-index.html">blogged</a> about the conference, and also took <a href="http://www.flickr.com/photos/jez/tags/groovy/">some pictures</a>, and wrote down a <a href="http://javanicus.com/blog2/items/166-index.html">few notes</a> about the conference.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>IntelliJ, as a team communication tool</title><link>https://glaforge.dev/posts/2004/11/25/intellij-as-a-team-communication-tool/</link><pubDate>Thu, 25 Nov 2004 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2004/11/25/intellij-as-a-team-communication-tool/</guid><description>&lt;p>Everyday, when you work with your team mates, you exchange information through different communication channels. If you work in the same offices, you can simply speak and make stand-up meetings. If you work with different teams spread across different places, different buildings, or even different countries&amp;ndash;especially true for Open Source projects&amp;ndash;you can pick up your (&lt;a href="http://www.skype.com/">Skype&lt;/a>) phone, and write emails, or chat through instant messenging or IRC.&lt;/p>
&lt;p>But sometimes, it feels a bit frustrating to use those archaic mediums. When you wish to share some code snippets, to study a stacktrace another developer got, to know on which files other developers are working on, the usual communication means feel less handy, less intuitive to use. So what could we do to improve that?&lt;/p></description><content:encoded>
<![CDATA[<p>Everyday, when you work with your team mates, you exchange information through different communication channels. If you work in the same offices, you can simply speak and make stand-up meetings. If you work with different teams spread across different places, different buildings, or even different countries&ndash;especially true for Open Source projects&ndash;you can pick up your (<a href="http://www.skype.com/">Skype</a>) phone, and write emails, or chat through instant messenging or IRC.</p>
<p>But sometimes, it feels a bit frustrating to use those archaic mediums. When you wish to share some code snippets, to study a stacktrace another developer got, to know on which files other developers are working on, the usual communication means feel less handy, less intuitive to use. So what could we do to improve that?</p>
<p>I&rsquo;ve been thinking about that topic for a few years already, but so far, I&rsquo;ve never found anything to my taste, and I somehow thought nobody had the same vision as I have. I believe we can do something to enhance our development tools to leverage the communication means we use everyday, but in a suboptimal way. Here comes <a href="http://www.maxkir.com/kirblog/">Kirill Maximov</a>, a <a href="http://www.jetbrains.com/idea">JetBrains</a> developer, working in the <a href="http://www.jetbrains.com/fabrique">Fabrique </a>team. Kirill is starting the development of a cross-IDE plugin: a communication tool integrated within your IDE. He blogged about it on his weblog: he discusses the <a href="http://www.maxkir.com/kirblog/2004/11/developers-communications-tool.html">ideas</a> we brainstormed together, and provides us with a<a href="http://www.maxkir.com/kirblog/2004/11/features-outline.html">roadmap</a>.</p>
<p>For the begining, the plugin should work on IntelliJ IDEA only, but it may be ported to other IDEs in the long term. And it will provide some basic features, such as:</p>
<ul>
<li>IDEA as a primary platform</li>
<li>using P2P transports (IP-multicast as a first channel)</li>
<li>finding your online team mates</li>
<li>showing the presence status</li>
<li>managing user rosters</li>
<li>exchanging text messages (standard IM feature)</li>
<li>exchanging Java stacktraces</li>
<li>viewing files currently edited by other colleagues</li>
</ul>
<p>In the following releases, some more high-level functionalities could be implemented:</p>
<ul>
<li>porting to other IDEs (Eclipse, JBuilder or NetBeans)</li>
<li>using other transport layers (Jabber, P2P)</li>
<li>viewing idle time</li>
<li>broadcasting messages to all team members or sub-teams</li>
<li>chating with multiple colleagues</li>
<li>exchanging code snippets</li>
<li>code navigation (click on the stacktrace and jump in your code!)</li>
<li>sending unit tests</li>
<li>pair programming (the extremos will like that!)</li>
<li>diffing local and remote files</li>
<li>subscribing to events (when a colleague starts editing the file you&rsquo;re working on)</li>
<li>etc.</li>
</ul>
<p>I&rsquo;m looking forward to play with your plugin, Kiril! I wish you good luck, and I hope I&rsquo;ll be able to help you spread the word, and potentially give a coding hand.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>On board JetBrains!</title><link>https://glaforge.dev/posts/2004/11/16/on-board-jetbrains/</link><pubDate>Tue, 16 Nov 2004 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2004/11/16/on-board-jetbrains/</guid><description>&lt;p>JetBrains, JetBrains&amp;hellip; You all know I love that company, and I love their products. Especially IntelliJ that I&amp;rsquo;ve using for a few years already as my main and primary Java IDE. That really, really rocks. And you&amp;rsquo;ve probably seen the picture of me wearing my &lt;a href="http://glaforge.free.fr/weblog/index.php?itemid=85">JetBrains TShirt&lt;/a>&amp;hellip;&lt;/p>
&lt;p>So what&amp;rsquo;s the news today? JetBrains&amp;rsquo;s just released their &lt;a href="http://www.onboard.jetbrains.com/">OnBoard monthly online magazine&lt;/a>. That&amp;rsquo;s worth a read! The featured articles are:&lt;/p>
&lt;ul>
&lt;li>Language Oriented Programming: The Next Programming Paradigm&lt;/li>
&lt;li>Applying Code Generation Approach in Fabrique&lt;/li>
&lt;li>IntelliJ IDEA: Structural Search and Replace, What, Why and How-to&lt;/li>
&lt;li>Extending Omea with New Resource Types&lt;/li>
&lt;/ul>
&lt;p>Of particular interest, Sergey Dmitriev&amp;rsquo;s&amp;ndash;long awaited for those in the know&amp;ndash;article about Meta-programming: &amp;ldquo;&lt;a href="http://www.onboard.jetbrains.com/articles/04/10/lop/">Language Oriented Programming&lt;/a>&amp;rdquo;. That&amp;rsquo;s a real paradigm shift!&lt;/p></description><content:encoded>
<![CDATA[<p>JetBrains, JetBrains&hellip; You all know I love that company, and I love their products. Especially IntelliJ that I&rsquo;ve using for a few years already as my main and primary Java IDE. That really, really rocks. And you&rsquo;ve probably seen the picture of me wearing my <a href="http://glaforge.free.fr/weblog/index.php?itemid=85">JetBrains TShirt</a>&hellip;</p>
<p>So what&rsquo;s the news today? JetBrains&rsquo;s just released their <a href="http://www.onboard.jetbrains.com/">OnBoard monthly online magazine</a>. That&rsquo;s worth a read! The featured articles are:</p>
<ul>
<li>Language Oriented Programming: The Next Programming Paradigm</li>
<li>Applying Code Generation Approach in Fabrique</li>
<li>IntelliJ IDEA: Structural Search and Replace, What, Why and How-to</li>
<li>Extending Omea with New Resource Types</li>
</ul>
<p>Of particular interest, Sergey Dmitriev&rsquo;s&ndash;long awaited for those in the know&ndash;article about Meta-programming: &ldquo;<a href="http://www.onboard.jetbrains.com/articles/04/10/lop/">Language Oriented Programming</a>&rdquo;. That&rsquo;s a real paradigm shift!</p>
<p>It&rsquo;s a pretty interesting vision. I&rsquo;m really keen on small DSL languages for specialized domain models. Moreover, it&rsquo;s also interesting from the point of view of meta-programming. That rings a bell in my mind, since <a href="http://groovy.codehaus.org/">Groovy</a> supports some meta-programming facilities, like our builders for instance.</p>
<p>On a side note, unrelated to the OnBoard magazine, I&rsquo;d like to say that JetBrains is very kind with Open Source projects. They&rsquo;ve just granted <a href="http://groovy.codehaus.org/">Groovy</a> developers a license. A nice gift!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Becoming an O'Reilly book author</title><link>https://glaforge.dev/posts/2004/11/07/becoming-an-o-reilly-book-author/</link><pubDate>Sun, 07 Nov 2004 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2004/11/07/becoming-an-o-reilly-book-author/</guid><description>&lt;p>A few months ago, &lt;a href="http://www.oreilly.com/">O&amp;rsquo;Reilly&lt;/a> contacted the &lt;a href="http://www.codehaus.org/">Codehaus&lt;/a> to find some Groovy experts for writing a book about &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a>, the scripting language for the JVM. I was very interested in writing such a book, and I asked another expert, Chris Poirier, if he&amp;rsquo;d fancy co-authoring the book with me, and he accepted. That&amp;rsquo;s how we started writing &amp;ldquo;Learning Groovy&amp;rdquo;, in the famous animals/learning series.&lt;/p>
&lt;p>This article is not about how to become an author, but rather, how O&amp;rsquo;Reilly helps you get up to speed with the task. Apart from helping you with your outline, with constant and useful feedback from your editors and so on, one of the things they do is to give you a nice welcome package as a new O&amp;rsquo;Reilly book author. Today, I just wanted to describe what they&amp;rsquo;ve just sent me.&lt;/p></description><content:encoded>
<![CDATA[<p>A few months ago, <a href="http://www.oreilly.com/">O&rsquo;Reilly</a> contacted the <a href="http://www.codehaus.org/">Codehaus</a> to find some Groovy experts for writing a book about <a href="http://groovy.codehaus.org/">Groovy</a>, the scripting language for the JVM. I was very interested in writing such a book, and I asked another expert, Chris Poirier, if he&rsquo;d fancy co-authoring the book with me, and he accepted. That&rsquo;s how we started writing &ldquo;Learning Groovy&rdquo;, in the famous animals/learning series.</p>
<p>This article is not about how to become an author, but rather, how O&rsquo;Reilly helps you get up to speed with the task. Apart from helping you with your outline, with constant and useful feedback from your editors and so on, one of the things they do is to give you a nice welcome package as a new O&rsquo;Reilly book author. Today, I just wanted to describe what they&rsquo;ve just sent me.</p>
<p>As I&rsquo;m writing a &ldquo;Learning&rdquo; series kind of book, my editor sent me two other Learning books: &ldquo;<a href="http://www.oreilly.com/catalog/learnjava2/">Learning Java</a>&rdquo;, and &ldquo;<a href="http://www.oreilly.com/catalog/lpython/">Learning Python</a>&rdquo;. That gives me some good references to take inspiration from.</p>
<p>But that&rsquo;s not all, after my contract was signed, they sent me a Welcome package. Here is what it contains:</p>
<ul>
<li>A letter from Tim O&rsquo;Reilly congratuling me, summarizing what the goal is, how he wrote his first book, how the editor&rsquo;s feedback is key, etc&hellip;</li>
<li>A really nice O&rsquo;Reilly TShirt with a &ldquo;Tarsier&rdquo; (the little animal with big round eyes from &ldquo;<a href="http://www.oreilly.com/catalog/unixnut3/">Unix in a Nutshell</a>&rdquo;),</li>
<li>A few O&rsquo;Reilly stickers (I&rsquo;m wondering where I could stick them though),</li>
<li>A document on how to best market our book,</li>
<li>A copy of &ldquo;<a href="http://www.amazon.com/exec/obidos/tg/detail/-/020530902X/qid=1084807900/sr=1-1/ref=sr_1_1/103-2263184-9239067?v=glance&amp;s=books">The Elements of Style</a>&rdquo;, from William Strunk Jr. and E.B. White,</li>
<li>A book on three audio CDs &ldquo;<a href="http://www.amazon.com/exec/obidos/tg/detail/-/0060006641/102-2174389-8230509?v=glance">On Writing Well</a>&rdquo;, from William Zinsser.</li>
<li>A little book about the O&rsquo;Reilly animals, showing some of the animals used on their book covers.</li>
</ul>
<p>I think that&rsquo;s a really nice Welcome package, when you start writing your first book, especially when English is not your mother tongue. &ldquo;<a href="http://www.amazon.com/exec/obidos/tg/detail/-/020530902X/qid=1084807900/sr=1-1/ref=sr_1_1/103-2263184-9239067?v=glance&amp;s=books">The Elements of Style</a>&rdquo; is a real reference in the field. Good choice. I knew it by reputation, but now, I&rsquo;ll be able to read it myself.</p>
<p>Do you want to know what other great advantage of being an author at O&rsquo;Reilly there is? We&rsquo;re granted a free access on <a href="http://safari.oreilly.com/">Safari</a>, their online bookshelf. Really, really cool!</p>
<p>So, thank you O&rsquo;Reilly, Mike and Brett for your feedback, and for all these gifts.</p>
<p>I&rsquo;m a happy author!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Pair Wiki-ing</title><link>https://glaforge.dev/posts/2004/10/06/pair-wiki-ing/</link><pubDate>Wed, 06 Oct 2004 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2004/10/06/pair-wiki-ing/</guid><description>&lt;p>Ever had to work collaboratively, concurrently on the same document in real-time? So far, the offering for dealing with collaborative documents authoring is rather oriented towards an asynchronous mode.&lt;/p>
&lt;p>You have Microsoft Word which allows you to make revisions, changes, highlights. You can use the good old email system by sending mails in a ping-pong way (no concurrent modifications allowed). You may also use a versioning system such as CVS or Subversion to edit documents in parallel and merge both work copies.&lt;/p></description><content:encoded>
<![CDATA[<p>Ever had to work collaboratively, concurrently on the same document in real-time? So far, the offering for dealing with collaborative documents authoring is rather oriented towards an asynchronous mode.</p>
<p>You have Microsoft Word which allows you to make revisions, changes, highlights. You can use the good old email system by sending mails in a ping-pong way (no concurrent modifications allowed). You may also use a versioning system such as CVS or Subversion to edit documents in parallel and merge both work copies.</p>
<p>The lucky Mac owners also have SubEthaEdit which makes real-time modifications possible on the same document. But if you&rsquo;re under Windows, as far as I know, there&rsquo;s no application available which really provides such a level of collaborative features. I&rsquo;ll have to hear from persons using SubEthaEdit over the net, rather than over Wifi hotspots&hellip; Is it possible by the way?</p>
<p><a href="http://www.javanicus.com/blog2/">Jeremy Rayner</a> has created a little Java application called <a href="http://javanicus.com/blog2/items/113-index.html">NetPad</a> that offers some real-time capabilities. But as we tried it over the net, it&rsquo;s not yet ideal, due to some network issues (time-outs and such, problem of content syncing when buffers differ).</p>
<p>By the way, today&rsquo;s Jeremy&rsquo;s Birthday! He&rsquo;s 0x20 years old! So, Mr Rayner, I wish you a very happy birthday, full of joy and geek gifts!</p>
<p>Jeremy and I had to work together on a document yesterday night. We are preparing a London meeting for the Groovy&rsquo;s JSR Expert Group and Groovy developers which will take place there in mid-november. We tried NetPad but weren&rsquo;t very successful, so we decided to have a mixed approach: chatting on IRC and at the same time using this little marvel called <a href="http://javanicus.com/blog2/items/113-index.html">Biscuit Barrel</a>. Biscuit is an incredible simple and light Wiki application, leveraging <a href="http://groovy.codehaus.org/">Groovy</a> scripting and <a href="http://radeox.org/space/start">Radeox</a> wiki-markup rendering from <a href="http://snipsnap.org/space/start">SnipSnap</a> and Confluence fame. With it, you can even write dynamic macros, create dynamic pages and templates, integrate SiteMesh, etc&hellip; (there are obviously some professional wikis with such features as well, like <a href="http://www.xwiki.org/xwiki/bin/view/Main/WebHome">XWiki</a>: a very powerful GPL wiki). Though not a prime-time or scalable Wiki, Biscuit is pretty neat and snappy to use on your localhost, or within an intranet.</p>
<p>So Jeremy and I chatted on #groovy yesterday. We were editing a Biscuit page in a close to real-time manner. And IRC was used to pass each other the editing token. Once one of the two was &ldquo;editing&rdquo;, the other should not edit it. Once the editor is &ldquo;done&rdquo;, you can take the &ldquo;editing&rdquo; token. Simple solution for a complex system! With this brainstorming taking place, and a wiki to take notes, we really managed to have a very productive and entertaining session. Thus for near real-time collaboration on a document, it can be as simple as using IRC+Wiki.</p>
<p>How are you guys doing real-time collaborative authoring? Any tips, any software to try for that kind of constraints? I&rsquo;m all ears and wish that a better solution exists. Otherwise I&rsquo;ll have to create my own some day!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy's birthday and news</title><link>https://glaforge.dev/posts/2004/08/26/groovy-s-birthday-and-news/</link><pubDate>Thu, 26 Aug 2004 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2004/08/26/groovy-s-birthday-and-news/</guid><description>&lt;p>A year ago, &lt;a href="http://radio.weblogs.com/0112098/">James Strachan&lt;/a> and &lt;a href="http://blogs.codehaus.org/people/bob/">Bob McWhirter&lt;/a> gave birth to &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a>: a dynamic and agile scripting language for the JVM. Nobody really knows who is the father, and who is the mother, neither do we know how the fecundation happened. Anyhow, James always used to say that it was all Bob&amp;rsquo;s fault, but indeed, James had so many groovy ideas that it&amp;rsquo;s hard to believe it&amp;rsquo;s not his own fault. He kept saying: &amp;ldquo;Wouldn&amp;rsquo;t it be groovy if we could do this and that&amp;hellip;&amp;rdquo;. Hence the name &amp;ldquo;Groovy&amp;rdquo;. That&amp;rsquo;s roughly how it all started.&lt;/p></description><content:encoded>
<![CDATA[<p>A year ago, <a href="http://radio.weblogs.com/0112098/">James Strachan</a> and <a href="http://blogs.codehaus.org/people/bob/">Bob McWhirter</a> gave birth to <a href="http://groovy.codehaus.org/">Groovy</a>: a dynamic and agile scripting language for the JVM. Nobody really knows who is the father, and who is the mother, neither do we know how the fecundation happened. Anyhow, James always used to say that it was all Bob&rsquo;s fault, but indeed, James had so many groovy ideas that it&rsquo;s hard to believe it&rsquo;s not his own fault. He kept saying: &ldquo;Wouldn&rsquo;t it be groovy if we could do this and that&hellip;&rdquo;. Hence the name &ldquo;Groovy&rdquo;. That&rsquo;s roughly how it all started.</p>
<p>I wasn&rsquo;t involved in Groovy in the early days, but I came across groovy by reading some blog posts here and there. About a month after the first commits on Codehaus&rsquo; CVS repository (Aug 28th), I looked closely at the code and at the (old) Groovy wiki. I read some interesting ideas, and I really got excited by that project. I really felt like it could fill some gaps in our J2EE stack and tool chain.</p>
<p>By that time, I was working for a company which was building a nice offering in the MDA market: you design a data-model with a Swing data-modeling application, you deploy this &ldquo;metamodel&rdquo; on the database of a kernel webapp hosted in a servlet container. And here you go, you have a working data-centric application, with complex and customisable list and item views, powerful search capabilities spanning several kinds of business objects, fine-grained role and profile authorisations, etc&hellip; In the developer version of this offering, there will be means for creating your own UI widgets that you can use in the forms used to edit your items. Those UI widgets are simply Java classes. But in order to create/edit/deploy them, the process is rather long and tedious, since you have to restart your webapp to reload them. At that point of the story, I really thought it would be nice to use Groovy to develop those widgets, without having to compile them and to reload your webapp. A nice gain at development time (especially since the customers themselves can write their own widgets).</p>
<p>There are many places where you can benefit from scripting languages in your projects. Whether it be Groovy, or any other scripting language for the JVM. I fell in love with some of the Groovy features, such as <a href="http://wiki.codehaus.org/groovy/SyntaxForTuplesListsMaps">native syntax for lists and maps</a>, <a href="http://groovy.codehaus.org/closures.html">closures</a>, markup, etc.</p>
<p>In decemeber 2003, I got involved in the development of Groovy, after I had submitted a few patches to improve Groovy&rsquo;s cleverness with regards to charsets and file encodings. Since then, I did various things, but a big part of my work was with all those <a href="http://groovy.codehaus.org/groovy-jdk.html">little groovy methods</a> that help us being more productive. Things like <code>String.padLeft()</code> to <code>Url.getText()</code>, etc&hellip;</p>
<p>So what&rsquo;s the status right now, after one year? Groovy is already quite stable. It&rsquo;s not yet perfect, some features are still missing, but I use it on a daily basis to write some scripts, such as flushing some JMS queues, retrieving web resources and parse them to monitor particular changes, and I&rsquo;m doing many other things with it as well.</p>
<p>A few months ago, <a href="http://www.jcp.org/en/jsr/detail?id=241">JSR-241</a> was submitted to the JCP. This process will enable us to have a more formal process to define the language and will provide a default reference implementation. It will also give wider acceptance and trust, so that developers around the world may integrate Groovy within their projects with confidence. This process is currently moving slowly but surely, development of Groovy is also evolving accordingly till we settle on some language design decisions. But already, we&rsquo;re approaching consensus on some of the ideas which are debated. In the meantime, some nice byte-code optimsations have been done for instance, some great features may see the light of day in the future, such as AST macros, etc. Stay tuned!</p>
<p>The IDE integration is moving too: <a href="http://roller.anthonyeden.com/page/zohar">Zohar Melamed</a> is working on the Eclipse plugin to make it work on Eclipse 3. A lot of changes happened in Eclipse&rsquo;s APIs which make this porting effort tougher than expected. And on my side, I&rsquo;m teaming up with Franck Rasolo (developer of the <a href="http://plugins.intellij.net/plugins/view/?id=Testdox%20IDEA%20Plugin">TestDox IDEA plugin</a>) to create a brand new full-featured IDEA IntelliJ plugin. So as I said in the previous paragraph: stay tuned!</p>
<p>For those who would like to learn Groovy by the book, you&rsquo;ll be glad to learn that different books are in the works at O&rsquo;Reilly, Manning and other publishers. <a href="http://www.wilson.co.uk/">John Wilson</a> is writing a &ldquo;Groovy in Action&rdquo;, while Chris Poirier and myself are working on a &ldquo;Learning Groovy&rdquo;. In mid 2005, you should have a lot to read!</p>
<p>After this little &ldquo;status message&rdquo;, I wish a happy birthday to Groovy. I hope that all the developers already playing with Groovy and embedding it within their own apps are happy with it. Thanks again to James and Bob for having given birth to such a cute and useful project, thanks to all those great developers and friends I met thanks to this project. I wish you all a Groovy Birthday!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Une huile de Vézelay</title><link>https://glaforge.dev/posts/2004/07/15/une-huile-de-v-zelay/</link><pubDate>Thu, 15 Jul 2004 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2004/07/15/une-huile-de-v-zelay/</guid><description>&lt;p>&lt;figure>
&lt;a href="#img-675ab0e39b713692fbb74ecfd72fdb24">
&lt;img src="https://glaforge.dev/img/misc/vezelay.jpg"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-675ab0e39b713692fbb74ecfd72fdb24">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/vezelay.jpg"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>Pour mes premiers essais de peinture à l&amp;rsquo;huile, j&amp;rsquo;ai choisi une vue plutôt originale de la région de Vézelay (dans l&amp;rsquo;Yonne), en prenant le contre-pied des angles classiques qui se tournent vers la basilique. J&amp;rsquo;ai décidé de peindre le point de vue que l&amp;rsquo;on voit à partir des hauteurs.&lt;/p>
&lt;p>La peinture à l&amp;rsquo;huile est un médium particulièrement agréable à peindre. Les mélanges des couleurs dans le vif donnent des effets intéressants, de même que la superposition de couches, gras sur maigre, avec un fond bien sec. En effet, j&amp;rsquo;ai réalisé ce tableau en deux passes : une première pour le fond (ciel, champs et forêt), puis une seconde couche pour les feuillages des arbres, ainsi que le petit village.&lt;/p></description><content:encoded>
<![CDATA[<p><figure>
  <a href="#img-675ab0e39b713692fbb74ecfd72fdb24">
    <img src="/img/misc/vezelay.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-675ab0e39b713692fbb74ecfd72fdb24">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/vezelay.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Pour mes premiers essais de peinture à l&rsquo;huile, j&rsquo;ai choisi une vue plutôt originale de la région de Vézelay (dans l&rsquo;Yonne), en prenant le contre-pied des angles classiques qui se tournent vers la basilique. J&rsquo;ai décidé de peindre le point de vue que l&rsquo;on voit à partir des hauteurs.</p>
<p>La peinture à l&rsquo;huile est un médium particulièrement agréable à peindre. Les mélanges des couleurs dans le vif donnent des effets intéressants, de même que la superposition de couches, gras sur maigre, avec un fond bien sec. En effet, j&rsquo;ai réalisé ce tableau en deux passes : une première pour le fond (ciel, champs et forêt), puis une seconde couche pour les feuillages des arbres, ainsi que le petit village.</p>
<p>A noter que la peinture n&rsquo;était pas sèche lorsque j&rsquo;ai pris la photo, c&rsquo;est pour cela que l&rsquo;on voit quelques reflets sur la gauche du tableau.</p>
<p>Pour en connaître plus sur Vézelay, vous pouvez consulter le <a href="http://www.ville-vezelay.com/">site de la ville</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Continuous Integration with DamageControl</title><link>https://glaforge.dev/posts/2004/07/06/continuous-integration-with-damagecontrol/</link><pubDate>Tue, 06 Jul 2004 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2004/07/06/continuous-integration-with-damagecontrol/</guid><description>&lt;p>&lt;a href="http://damagecontrol.codehaus.org/">DamageControl&lt;/a>, I believe, is one of the great tools an Open Source project must have. For those who don&amp;rsquo;t know this great project hosted at &lt;a href="http://www.codehaus.org/">Codehaus&lt;/a>, it is one incarnation of a continuous integration system, like CruiseControl or others&amp;hellip;&lt;/p>
&lt;p>Basically, this is a tool which allow developers to make their project build automatically upon each commit to ease integration. Each time a modification is done on your Source Control Management system, it triggers a build. It&amp;rsquo;s particularly handy when associated with a full build including test suites.&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://damagecontrol.codehaus.org/">DamageControl</a>, I believe, is one of the great tools an Open Source project must have. For those who don&rsquo;t know this great project hosted at <a href="http://www.codehaus.org/">Codehaus</a>, it is one incarnation of a continuous integration system, like CruiseControl or others&hellip;</p>
<p>Basically, this is a tool which allow developers to make their project build automatically upon each commit to ease integration. Each time a modification is done on your Source Control Management system, it triggers a build. It&rsquo;s particularly handy when associated with a full build including test suites.</p>
<p>In real-time, you&rsquo;re advertized if the build succeeds or not, through different means, be it through mail, IRC or Instant Messenging.</p>
<p>Thanks to DamageControl, I can see that my last commit on <a href="http://groovy.codehaus.org/">Groovy</a> was successful. And even better, you can put on your web site a link to a picture indicating that the build succeeded or failed. If it&rsquo;s green, that&rsquo;s cool!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>JetBrains fashion victim</title><link>https://glaforge.dev/posts/2004/06/12/jetbrains-fashion-victim/</link><pubDate>Sat, 12 Jun 2004 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2004/06/12/jetbrains-fashion-victim/</guid><description>&lt;p>&lt;figure>
&lt;a href="#img-1bd984ea05a5a48fa3129258a4c90d1c">
&lt;img src="https://glaforge.dev/img/misc/Guillaume-JetBrains.jpg"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-1bd984ea05a5a48fa3129258a4c90d1c">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/Guillaume-JetBrains.jpg"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p>
&lt;p>I am a &lt;a href="http://www.jetbrains.com/">JetBrains&lt;/a> fashion victim. I&amp;rsquo;ve just received my JetBrains TShirt this morning.&lt;/p>
&lt;p>It was quite early (say 8 o&amp;rsquo;clock) for a saturday when I&amp;rsquo;m used to wake up late late late, especially after coding &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> late at night&amp;hellip; when that damn postman rang at the door. I wanted to slaughter this bastard for waking me up that early&amp;hellip; I even wanted to not open the door. But I remembered that I was supposed to receive a TShirt some day, thanks to my participation in a very private society of IntelliJ IDEA affictionados called &amp;ldquo;Team JetBrains&amp;rdquo;&amp;hellip;&lt;/p></description><content:encoded>
<![CDATA[<p><figure>
  <a href="#img-1bd984ea05a5a48fa3129258a4c90d1c">
    <img src="/img/misc/Guillaume-JetBrains.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-1bd984ea05a5a48fa3129258a4c90d1c">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/Guillaume-JetBrains.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>I am a <a href="http://www.jetbrains.com/">JetBrains</a> fashion victim. I&rsquo;ve just received my JetBrains TShirt this morning.</p>
<p>It was quite early (say 8 o&rsquo;clock) for a saturday when I&rsquo;m used to wake up late late late, especially after coding <a href="http://groovy.codehaus.org/">Groovy</a> late at night&hellip; when that damn postman rang at the door. I wanted to slaughter this bastard for waking me up that early&hellip; I even wanted to not open the door. But I remembered that I was supposed to receive a TShirt some day, thanks to my participation in a very private society of IntelliJ IDEA affictionados called &ldquo;Team JetBrains&rdquo;&hellip;</p>
<p>To make a long story short, JetBrains promised us (members of this secret and stealth community) to send us TShirts, but they promised long long ago&hellip; But nothing was ever coming. They had issues with the different TShirts makers they contacted and/or contracted. So at the end, we though we would never receive anything. Except our Personal License which we were given early when Aurora was out, thanks to our active participation in the <a href="http://www.intellij.net/eap">Early Access Programm</a>. The TShirt was our &ldquo;Arlésienne&rdquo;&hellip;</p>
<p>But indeed, the day has come, and I got this TShirt that you can see on the picture on the left: Yes, that&rsquo;s me who&rsquo;s wearing proudly that nice TShirt. This week was quite interesting also because I&rsquo;ve been awarded a <a href="http://gmail.google.com/">GMail</a> account, thanks to a little game of magic number that Alain Ravet launched. The game was a draw: both Dmitry Lomov (from JetBrains) and me were the closest to the magic number. But Alain&rsquo;s dog decided between the two. And Dmitry won. But Robert Beeger had another GMail invitation left, so he offered it to the second: me! Thank you Robert for your GMail invitation, I&rsquo;m very grateful to you.</p>
<p>Now it&rsquo;s time to have fun, so have a nice Week-end everybody. And thank you very much JetBrains for my TShirt.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>First OSS-Get Together in Paris</title><link>https://glaforge.dev/posts/2004/06/07/first-oss-get-together-in-paris/</link><pubDate>Mon, 07 Jun 2004 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2004/06/07/first-oss-get-together-in-paris/</guid><description>&lt;p>For the first time in Paris, an &lt;strong>&lt;em>Open Source Software Get-Together&lt;/em>&lt;/strong> was organized by &lt;a href="http://blogs.codehaus.org/people/vmassol/">Vincent Massol&lt;/a> of &lt;a href="http://jakarta.apache.org/cactus/">Cactus&lt;/a> fame and author of &lt;a href="http://www.manning.com/catalog/view.php?book=massol">JUnit in Action&lt;/a> book. It was a lot of fun to meet him and other French OSS developers. I though we weren&amp;rsquo;t numerous to work on Open Source projects in France, but after all, it seems I was wrong. Moreover, I was stunned to meet someone working for the same company as myself, and I didn&amp;rsquo;t even know him! The world is such a small place indeed.&lt;/p></description><content:encoded>
<![CDATA[<p>For the first time in Paris, an <strong><em>Open Source Software Get-Together</em></strong> was organized by <a href="http://blogs.codehaus.org/people/vmassol/">Vincent Massol</a> of <a href="http://jakarta.apache.org/cactus/">Cactus</a> fame and author of <a href="http://www.manning.com/catalog/view.php?book=massol">JUnit in Action</a> book. It was a lot of fun to meet him and other French OSS developers. I though we weren&rsquo;t numerous to work on Open Source projects in France, but after all, it seems I was wrong. Moreover, I was stunned to meet someone working for the same company as myself, and I didn&rsquo;t even know him! The world is such a small place indeed.</p>
<p>We talked about our different projects on which we are working at night, on our free time. Almost half of us were <a href="http://maven.apache.org/">Maven</a> developers or contributors (Emmanuel Venisse, Arnaud Heritier and Vincent Massol), the other developers present were working on <a href="http://easystruts.sourceforge.net/">EasyStruts</a> (Emmanuel Boudrant), <a href="http://www.smartcomps.org/twister/">Twister</a> (Matthieu Riou), <a href="http://jcaptcha.sourceforge.net/">JCaptcha</a> (Marc-Antoine Garrigue) and <a href="http://groovy.codehaus.org/">Groovy</a> (myself the Groovy Despot).</p>
<p>For this first meeting (others will come soon) Vincent invited us at the headquarters of <a href="http://www.pivolis.com/">Pivolis</a>, an innovating <em>pivotal provider</em> providing offshore development. After that, we went to a restaurant near <em>les Champs-Elysées</em>. We like this concept very much: revamping the world while eating and/or drinking is somewhat part of the French culture.</p>
<p>I was really glad to meet you all guys, and I&rsquo;m longing to meet you again soon. Now, for the pictures, please click on the <em>more</em> link just below:</p>
<p><figure>
  <a href="#img-9d961dc33070189f361f62a3490c7fae">
    <img src="/img/ossgtp/MAG-VM-Pivolis.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-9d961dc33070189f361f62a3490c7fae">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/ossgtp/MAG-VM-Pivolis.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p><figure>
  <a href="#img-af161b4911966ea6c6d8feb17e547b96">
    <img src="/img/ossgtp/EV-AH-Pivolis.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-af161b4911966ea6c6d8feb17e547b96">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/ossgtp/EV-AH-Pivolis.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
<br />
Emmanuel and Arnaud</p>
<p><figure>
  <a href="#img-3f9e5fb26f4ed3decd002e41cbd93d77">
    <img src="/img/ossgtp/EB-MR-Pivolis.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-3f9e5fb26f4ed3decd002e41cbd93d77">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/ossgtp/EB-MR-Pivolis.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
<br />
Emmanuel and Matthieu</p>
<p><figure>
  <a href="#img-a49cfce96455f897fda50ea4e1f42c98">
    <img src="/img/ossgtp/EV-VM-AH-Hippo.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-a49cfce96455f897fda50ea4e1f42c98">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/ossgtp/EV-VM-AH-Hippo.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
<br />
Emmanuel, Vincent and Arnaud</p>
<p><figure>
  <a href="#img-9985bf5990dbb21a521414ddea272f5b">
    <img src="/img/ossgtp/EB-MR-Hippo.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-9985bf5990dbb21a521414ddea272f5b">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/ossgtp/EB-MR-Hippo.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
<br />
Emmanuel and Matthieu</p>
<p><figure>
  <a href="#img-84acc60bcf90ec6d34b12d9b68066854">
    <img src="/img/ossgtp/MR-GL-Hippo.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-84acc60bcf90ec6d34b12d9b68066854">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/ossgtp/MR-GL-Hippo.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
<br />
Matthieu and Guillaume (doh! that&rsquo;s me!)</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Tip O' the day : SSH on Windows</title><link>https://glaforge.dev/posts/2004/05/07/tip-o-the-day-ssh-on-windows/</link><pubDate>Fri, 07 May 2004 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2004/05/07/tip-o-the-day-ssh-on-windows/</guid><description>&lt;p>My fellow readers might remember my problems with SSH which have often bothered me. I faced again a similar issue:&lt;/p>
&lt;p>I wanted to upload a new Groovy snapshot Jar, before we release RC-1 next week. As a Groovy despot, with the help of some wonderful hausmates (particularly Trygve and Bob) I managed to authenticate myself with ssh-agent + ssh-add on the Codehaus server, so that maven doesn&amp;rsquo;t require me to enter my passphrase for my key.&lt;/p></description><content:encoded>
<![CDATA[<p>My fellow readers might remember my problems with SSH which have often bothered me. I faced again a similar issue:</p>
<p>I wanted to upload a new Groovy snapshot Jar, before we release RC-1 next week. As a Groovy despot, with the help of some wonderful hausmates (particularly Trygve and Bob) I managed to authenticate myself with ssh-agent + ssh-add on the Codehaus server, so that maven doesn&rsquo;t require me to enter my passphrase for my key.</p>
<p>For the record, I&rsquo;m using Cygwin on a windows XP box, with OpenSSH installed.</p>
<p>Here is the trick, in a cygwin-bash terminal type:</p>
<pre tabindex="0"><code>eval `ssh-agent`
ssh-add
maven -Dmaven.username=xxxx yourGoal
</code></pre><p>And I was done&hellip;</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>New Groovy Snapshot</title><link>https://glaforge.dev/posts/2004/05/06/new-groovy-snapshot/</link><pubDate>Thu, 06 May 2004 02:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2004/05/06/new-groovy-snapshot/</guid><description>&lt;p>Since Groovy&amp;rsquo;s move from one server to another, we could not deliver the latest jars. We&amp;rsquo;re sorry for that, but we&amp;rsquo;re doing our best to fix all the little issues you&amp;rsquo;ve encountered (broken links, missing jar, etc) It&amp;rsquo;s not without difficulty that I managed to upload the latest snapshot of groovy, but I&amp;rsquo;m glad to announce that there is a new groovy-1.0-rc1-snapshot.jar.&lt;/p>
&lt;p>You can find it here:&lt;/p>
&lt;p>&lt;a href="http://dist.codehaus.org/groovy/dist/groovy/jars/">http://dist.codehaus.org/groovy/dist/groovy/jars/&lt;/a>&lt;/p>
&lt;p>If you&amp;rsquo;re still using beta-4, please upgrade to the latest snapshot. Though there are still some bugs we haven&amp;rsquo;t fixed yet regarding &amp;ldquo;verify errors&amp;rdquo; and such, this snapshot is already much more stable. Next week, we should release a new distribution.&lt;/p></description><content:encoded>
<![CDATA[<p>Since Groovy&rsquo;s move from one server to another, we could not deliver the latest jars. We&rsquo;re sorry for that, but we&rsquo;re doing our best to fix all the little issues you&rsquo;ve encountered (broken links, missing jar, etc) It&rsquo;s not without difficulty that I managed to upload the latest snapshot of groovy, but I&rsquo;m glad to announce that there is a new groovy-1.0-rc1-snapshot.jar.</p>
<p>You can find it here:</p>
<p><a href="http://dist.codehaus.org/groovy/dist/groovy/jars/">http://dist.codehaus.org/groovy/dist/groovy/jars/</a></p>
<p>If you&rsquo;re still using beta-4, please upgrade to the latest snapshot. Though there are still some bugs we haven&rsquo;t fixed yet regarding &ldquo;verify errors&rdquo; and such, this snapshot is already much more stable. Next week, we should release a new distribution.</p>
<p>Enjoy.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Seen on maven IRC</title><link>https://glaforge.dev/posts/2004/05/06/seen-on-maven-irc/</link><pubDate>Thu, 06 May 2004 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2004/05/06/seen-on-maven-irc/</guid><description>&lt;p>Being a groovy despot, I was in the process of redeploying groovy&amp;rsquo;s web site and also the latest snapshot jars of the distribution which a lot of guys have asked for. But I was having some problems with my setup, ssh connection, maven goals, etc. With Trygve and Bob, we were chatting about that, and here is an interesting snippet of the ongoing discussion:&lt;/p>
&lt;blockquote>
&lt;p>trygvis: bob: dist is a recursive symlink
bob: yes, I know
bob: it&amp;rsquo;s broken
bob: I&amp;rsquo;ll fix it
bob: and I&amp;rsquo;ll smack the next person who tells me about it
trygvis: DO IT NOW! [Guillaume] LOL :-)))
bob: FUCK YOU
trygvis: OK!!&lt;/p></description><content:encoded>
<![CDATA[<p>Being a groovy despot, I was in the process of redeploying groovy&rsquo;s web site and also the latest snapshot jars of the distribution which a lot of guys have asked for. But I was having some problems with my setup, ssh connection, maven goals, etc. With Trygve and Bob, we were chatting about that, and here is an interesting snippet of the ongoing discussion:</p>
<blockquote>
<p>trygvis: bob: dist is a recursive symlink
bob: yes, I know
bob: it&rsquo;s broken
bob: I&rsquo;ll fix it
bob: and I&rsquo;ll smack the next person who tells me about it
trygvis: DO IT NOW! [Guillaume] LOL :-)))
bob: FUCK YOU
trygvis: OK!!</p></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>IntelliJ : SSH2 finally finds its way...</title><link>https://glaforge.dev/posts/2004/04/30/intellij-ssh2-finally-finds-its-way/</link><pubDate>Fri, 30 Apr 2004 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2004/04/30/intellij-ssh2-finally-finds-its-way/</guid><description>&lt;p>I&amp;rsquo;ve been waiting for that for months already. I&amp;rsquo;ve been whining and whining again about the lack of an internal SSH2 implementation in IntelliJ. And now, it is there!&lt;/p>
&lt;p>As you perhaps already know by reading my weblog, I&amp;rsquo;m a &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> developper. And the Groovy project is hosted at &lt;a href="http://www.codehaus.org/">Codehaus&lt;/a> which makes use of SSH2 for accessing the CVS repository.&lt;/p>
&lt;p>I had tried, but without success, &lt;a href="http://glaforge.free.fr/weblog/index.php?itemid=63">different alternatives&lt;/a>, such as using Putty/pling/pageant, but I didn&amp;rsquo;t manage to create a connection. So I ended up using Thomas Singer&amp;rsquo;s wonderful &lt;a href="http://www.smartcvs.com/">SmartCVS&lt;/a>. This tools sort of &lt;a href="http://glaforge.free.fr/weblog/index.php?itemid=64">saved my life&lt;/a>. I&amp;rsquo;m very grateful to you Tom&amp;hellip;&lt;/p></description><content:encoded>
<![CDATA[<p>I&rsquo;ve been waiting for that for months already. I&rsquo;ve been whining and whining again about the lack of an internal SSH2 implementation in IntelliJ. And now, it is there!</p>
<p>As you perhaps already know by reading my weblog, I&rsquo;m a <a href="http://groovy.codehaus.org/">Groovy</a> developper. And the Groovy project is hosted at <a href="http://www.codehaus.org/">Codehaus</a> which makes use of SSH2 for accessing the CVS repository.</p>
<p>I had tried, but without success, <a href="http://glaforge.free.fr/weblog/index.php?itemid=63">different alternatives</a>, such as using Putty/pling/pageant, but I didn&rsquo;t manage to create a connection. So I ended up using Thomas Singer&rsquo;s wonderful <a href="http://www.smartcvs.com/">SmartCVS</a>. This tools sort of <a href="http://glaforge.free.fr/weblog/index.php?itemid=64">saved my life</a>. I&rsquo;m very grateful to you Tom&hellip;</p>
<p>But now, IntelliJ supports SSH2 out of the box! At first glance, I didn&rsquo;t even noticed, because I was used to see the different supported methods: pserver, local, ext and SSH. But Robert S. Sfeir showed me the way on the newsgroups by telling me it was there&hellip; Uh? He must be kidding ? Well, no, he wasn&rsquo;t, when in the CVS SSH panel, now there&rsquo;s a radio button to choose between SSH1 and SSH2, and you can choose your private key. The only little omission though, is that you have to type by hand the full type to your private key because there&rsquo;s no button for choosing the path from a dialog box. But I&rsquo;m sure it&rsquo;ll be fixed in the next release.</p>
<p>Thank you JetBrains (and in particular Olesya) for your great work on such a great product!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>A groovy web server</title><link>https://glaforge.dev/posts/2004/04/23/a-groovy-web-server/</link><pubDate>Fri, 23 Apr 2004 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2004/04/23/a-groovy-web-server/</guid><description>&lt;p>Based on a suggestion made by Jamie McCrindle, I decided it was time to add some missing &lt;a href="http://groovy.codehaus.org/groovy-jdk.html">Groovy methods&lt;/a> related to sockets.&lt;/p>
&lt;p>So far, I have added a &lt;a href="http://glaforge.free.fr/weblog/index.php?itemid=74">bunch of IO/streams methods&lt;/a> enhancing the JDK core classes, but there were no methods dealing with sockets. But now, this time is over.&lt;/p>
&lt;p>I have added two methods:&lt;/p>
&lt;ul>
&lt;li>Socket.withStreams(Closure) which takes a closure as argument, and has acces to an input stream and an output stream, and&lt;/li>
&lt;li>ServerSocket.accept(Closure) which takes a closure argument which uses a socket as argument&lt;/li>
&lt;/ul>
&lt;p>What&amp;rsquo;s better than a sample code to illustrate that ? Hey, we&amp;rsquo;re going to implement a simplistic Hello World web server. Here it is&amp;hellip;&lt;/p></description><content:encoded>
<![CDATA[<p>Based on a suggestion made by Jamie McCrindle, I decided it was time to add some missing <a href="http://groovy.codehaus.org/groovy-jdk.html">Groovy methods</a> related to sockets.</p>
<p>So far, I have added a <a href="http://glaforge.free.fr/weblog/index.php?itemid=74">bunch of IO/streams methods</a> enhancing the JDK core classes, but there were no methods dealing with sockets. But now, this time is over.</p>
<p>I have added two methods:</p>
<ul>
<li>Socket.withStreams(Closure) which takes a closure as argument, and has acces to an input stream and an output stream, and</li>
<li>ServerSocket.accept(Closure) which takes a closure argument which uses a socket as argument</li>
</ul>
<p>What&rsquo;s better than a sample code to illustrate that ? Hey, we&rsquo;re going to implement a simplistic Hello World web server. Here it is&hellip;</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span> <span style="color:#0e84b5;font-weight:bold">java.net.*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#902000">def</span> server <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> ServerSocket<span style="color:#666">(</span><span style="color:#40a070">9991</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">while</span><span style="color:#666">(</span><span style="color:#007020;font-weight:bold">true</span><span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>  server<span style="color:#666">.</span><span style="color:#4070a0">accept</span> <span style="color:#666">{</span> socket <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>    socket<span style="color:#666">.</span><span style="color:#4070a0">withStreams</span> <span style="color:#666">{</span> input<span style="color:#666">,</span> output <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#007020;font-weight:bold">try</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        input<span style="color:#666">.</span><span style="color:#4070a0">eachLine</span> <span style="color:#666">{</span> line <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>          println line
</span></span><span style="display:flex;"><span>          <span style="color:#06287e">if</span> <span style="color:#666">(</span>line<span style="color:#666">.</span><span style="color:#4070a0">length</span><span style="color:#666">()</span> <span style="color:#666">==</span> <span style="color:#40a070">0</span><span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>            <span style="color:#007020;font-weight:bold">throw</span> <span style="color:#007020;font-weight:bold">new</span> <span style="color:#06287e">GroovyRuntimeException</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>          <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>        <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>      <span style="color:#666">}</span> <span style="color:#007020;font-weight:bold">catch</span> <span style="color:#666">(</span>GroovyRuntimeException b<span style="color:#666">)</span> <span style="color:#666">{</span> <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>      output<span style="color:#666">.</span><span style="color:#4070a0">withWriter</span> <span style="color:#666">{</span> writer <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>        writer <span style="color:#666">&lt;&lt;</span> <span style="color:#4070a0">&#34;HTTP/1.1 200 OK\n&#34;</span>
</span></span><span style="display:flex;"><span>        writer <span style="color:#666">&lt;&lt;</span> <span style="color:#4070a0">&#34;Content-Type: text/html\n\n&#34;</span>
</span></span><span style="display:flex;"><span>        writer <span style="color:#666">&lt;&lt;</span> <span style="color:#4070a0">&#34;Hello World!&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span>  <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Enjoy!</p>
<p>Thank you Jamie for your code ;-)</p>
<p><strong>Update 2013/02/20:</strong> I&rsquo;ve tweaked the above script to use the proper closure syntax that we settled with in Groovy 1.0</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>First steps in TDD-land</title><link>https://glaforge.dev/posts/2004/03/15/first-steps-in-tdd-land/</link><pubDate>Mon, 15 Mar 2004 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2004/03/15/first-steps-in-tdd-land/</guid><description>&lt;p>Unit tests aren&amp;rsquo;t really new for me, but so far, on the different projets on which I worked recently, I haven&amp;rsquo;t really had the opportunity to develop &amp;ldquo;test firt&amp;rdquo;. Moreover, I could not test much because those projects were not pretty test-friendly (static instances all around, nothing close to IoC/DI anywhere around). And also, on web-based and GUI projects, it is not that easy to write tests (when I have time, I should definitely have a look at those HTTP and Swing testing frameworks). Because of those projects, I was quite used to test things here and there with main methods and System.out.println()&amp;hellip; you&amp;rsquo;ll agree with me that it&amp;rsquo;s not really professional. Not easy to make regression tests as well. Shame on me!&lt;/p></description><content:encoded>
<![CDATA[<p>Unit tests aren&rsquo;t really new for me, but so far, on the different projets on which I worked recently, I haven&rsquo;t really had the opportunity to develop &ldquo;test firt&rdquo;. Moreover, I could not test much because those projects were not pretty test-friendly (static instances all around, nothing close to IoC/DI anywhere around). And also, on web-based and GUI projects, it is not that easy to write tests (when I have time, I should definitely have a look at those HTTP and Swing testing frameworks). Because of those projects, I was quite used to test things here and there with main methods and System.out.println()&hellip; you&rsquo;ll agree with me that it&rsquo;s not really professional. Not easy to make regression tests as well. Shame on me!</p>
<p>As a pet project, everybody must have one I guess, I&rsquo;m a developer on the <a href="http://groovy-lang.org/">Groovy</a> scripting language. Each time we develop something, we always test it, thanks to the geniusly designed Groovy testing framework settled by James. So I&rsquo;m quite used to unit testing my work. But most of the time, even in Groovy, I code first, then test afterwards. I haven&rsquo;t taken the habit of writing the test first, then trying my best to make it pass. But recently, James improved the build process, so it&rsquo;s going to be easier to work test firt though (ability to launch a single test, and a quick-test maven goal was added).</p>
<p>Things are changing, at my new company, I&rsquo;m starting a new project: a JMS implementation over a MOM (Message-Oriented Middleware). And I ceized the opportunity to make my first steps in TDD-land. And I must admit that I&rsquo;m quite satisfied with this approach.</p>
<p>In order to retrieve the administered objects from a JNDI context, I created my own little embeded JNDI provider. That&rsquo;s my first real test-first piece of software. As I didn&rsquo;t know much of the implementation of such a beast, I thought testing it first was a good idea. And indeed it is: testing first allows me to guide my hand and my brain during the development process. As I discover the spec, as I read the Javadoc, I create tests that match those requirements, then try to implement them. This method should throw an NPE if the Name object is null? Ok, a new test. This NamingEnumeration.hasMore() method should return false if there&rsquo;s no more elements? Ok, once again a new test. At the end of the day, I have a fair amount of tests that correspond to the spec I must implement.</p>
<p>I won&rsquo;t detail more my implementation, but simply wanted to express my delighted feeling. I must admit that TDD is quite a pleasant and fruitful experience. And I can now understand better people infected by the test-first virus. As I code, as I understand better what I&rsquo;m supposed to implement, the tests guide me and make me more confident with my code. With the nice JUnit integration within <a href="http://www.jetbrains.com/">IntelliJ IDEA</a>(my favourite IDE and the best I&rsquo;ve used so far) I&rsquo;m feeling good when I see the famous green bar. As much as I can, and if I can convert my colleagues to the TDD approach, I&rsquo;ll be continuing writing JUnit tests first.</p>
<p>Another green bar, yes!!!</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Heads-up on File and Stream groovy methods</title><link>https://glaforge.dev/posts/2004/03/03/heads-up-on-file-and-stream-groovy-methods/</link><pubDate>Wed, 03 Mar 2004 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2004/03/03/heads-up-on-file-and-stream-groovy-methods/</guid><description>&lt;p>Over the week-end, I implemented new groovy methods, as defined in GROOVY-208 Plus a few complementary methods.&lt;/p>
&lt;ul>
&lt;li>&lt;code>getText()&lt;/code>:
&lt;ul>
&lt;li>&lt;code>BufferedReader.getText()&lt;/code>&lt;/li>
&lt;li>&lt;code>File.getText()&lt;/code>&lt;/li>
&lt;li>&lt;code>File.getText(encoding)&lt;/code>&lt;/li>
&lt;li>&lt;code>Process.getText()&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>You can now easily read the full content of a file or of a buffered reader and get it as a String.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-groovy" data-lang="groovy">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// retrieve the content of the file
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>&lt;span style="color:#902000">def&lt;/span> content &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020;font-weight:bold">new&lt;/span> File&lt;span style="color:#666">(&lt;/span>&lt;span style="color:#4070a0">&amp;#34;myFile.txt&amp;#34;&lt;/span>&lt;span style="color:#666">).&lt;/span>&lt;span style="color:#4070a0">text&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// you can specify the encoding of the file
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// note that since getText() has a parameter,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// you cannot call it with something like text(&amp;#34;UTF-8&amp;#34;)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>content &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020;font-weight:bold">new&lt;/span> File&lt;span style="color:#666">(&lt;/span>&lt;span style="color:#4070a0">&amp;#34;myFile.txt&amp;#34;&lt;/span>&lt;span style="color:#666">).&lt;/span>&lt;span style="color:#4070a0">getText&lt;/span>&lt;span style="color:#666">(&lt;/span>&lt;span style="color:#4070a0">&amp;#34;UTF-8&amp;#34;&lt;/span>&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>There is also a getText() method on Process which gathers the output of a process:&lt;/p></description><content:encoded>
<![CDATA[<p>Over the week-end, I implemented new groovy methods, as defined in GROOVY-208 Plus a few complementary methods.</p>
<ul>
<li><code>getText()</code>:
<ul>
<li><code>BufferedReader.getText()</code></li>
<li><code>File.getText()</code></li>
<li><code>File.getText(encoding)</code></li>
<li><code>Process.getText()</code></li>
</ul>
</li>
</ul>
<p>You can now easily read the full content of a file or of a buffered reader and get it as a String.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// retrieve the content of the file  
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span><span style="color:#902000">def</span> content <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> File<span style="color:#666">(</span><span style="color:#4070a0">&#34;myFile.txt&#34;</span><span style="color:#666">).</span><span style="color:#4070a0">text</span>  
</span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// you can specify the encoding of the file  
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// note that since getText() has a parameter,   
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// you cannot call it with something like text(&#34;UTF-8&#34;)   
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>content <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> File<span style="color:#666">(</span><span style="color:#4070a0">&#34;myFile.txt&#34;</span><span style="color:#666">).</span><span style="color:#4070a0">getText</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;UTF-8&#34;</span><span style="color:#666">)</span> 
</span></span></code></pre></div><p>There is also a getText() method on Process which gathers the output of a process:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>stringOutput <span style="color:#666">=</span> <span style="color:#4070a0">&#34;ls&#34;</span><span style="color:#666">.</span><span style="color:#4070a0">execute</span><span style="color:#666">().</span><span style="color:#4070a0">text</span> <span style="color:#60a0b0;font-style:italic">// Unices  
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>stringOutput <span style="color:#666">=</span> <span style="color:#4070a0">&#34;dir&#34;</span><span style="color:#666">.</span><span style="color:#4070a0">execute</span><span style="color:#666">().</span><span style="color:#4070a0">text</span> <span style="color:#60a0b0;font-style:italic">// Windows
</span></span></span></code></pre></div><ul>
<li><code>write()</code>:
<ul>
<li><code>File.write(text)</code></li>
<li><code>File.write(text, encoding)</code></li>
</ul>
</li>
</ul>
<p>As we&rsquo;ve defined read methods for reading the content of the file in one go, you can also write it in a single step as well. Note that write overwrite the previous content of the file. If you don&rsquo;t want the behaviour, use the append() method.</p>
<p>// write the text in the file<br />
new File(&ldquo;myFile.txt&rdquo;).write(&ldquo;Hello world&rdquo;)<br />
// you may also specify the encoding<br />
new File(&ldquo;myFile.txt&rdquo;).write(&quot;\u20AC: is the symbol for the Euro currency&quot;, &ldquo;UTF-16LE&rdquo;)</p>
<ul>
<li><code>append()</code>:
<ul>
<li><code>File.append(text)</code></li>
<li><code>File.append(text, encoding)</code></li>
</ul>
</li>
</ul>
<p>In the same spirit, I decided to add some append() methods to append text at the end of the File.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> f <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> File<span style="color:#666">(</span><span style="color:#4070a0">&#34;myFile.txt&#34;</span><span style="color:#666">).</span><span style="color:#4070a0">append</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;Hello World\\n&#34;</span><span style="color:#666">)</span>  
</span></span><span style="display:flex;"><span>f<span style="color:#666">.</span><span style="color:#4070a0">append</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;Hello again, that&#39;s me!&#34;</span><span style="color:#666">)</span>  
</span></span></code></pre></div><p>You can also specify the encoding. Of course, don&rsquo;t write in the same file with different encodings, no editors would be able to read it ;-)</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> f <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> File<span style="color:#666">(</span><span style="color:#4070a0">&#34;myFile.txt&#34;</span><span style="color:#666">).</span><span style="color:#4070a0">append</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;Hello World\\n&#34;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#34;UTF-16BE&#34;</span><span style="color:#666">)</span>  
</span></span><span style="display:flex;"><span>f<span style="color:#666">.</span><span style="color:#4070a0">append</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;Hello again, that&#39;s me!&#34;</span><span style="color:#666">,</span> <span style="color:#4070a0">&#34;UTF-16BE&#34;</span><span style="color:#666">)</span>
</span></span></code></pre></div><ul>
<li><code>newWrite()</code>:
<ul>
<li><code>File.newWriter(encoding)</code></li>
<li><code>File.newWriter(encoding, append)</code></li>
<li><code>File.newWriter(append)</code></li>
<li><code>File.newPrintWriter(encoding)</code></li>
</ul>
</li>
</ul>
<p>Missing newWriter() methods have been added. It&rsquo;s now possible to specify the encoding used to write files, and optionnaly to specify wether we&rsquo;re in append mode.</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> writer <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> File<span style="color:#666">(</span><span style="color:#4070a0">&#34;myText.txt&#34;</span><span style="color:#666">).</span><span style="color:#4070a0">newWriter</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;UTF-8&#34;</span><span style="color:#666">,</span> <span style="color:#007020;font-weight:bold">true</span><span style="color:#666">)</span>  
</span></span><span style="display:flex;"><span>writer<span style="color:#666">.</span><span style="color:#4070a0">write</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;\u20AC: euro symbol\n&#34;</span><span style="color:#666">)</span>  
</span></span><span style="display:flex;"><span>writer<span style="color:#666">.</span><span style="color:#4070a0">write</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;$: dollar symbol\n&#39;</span><span style="color:#666">)</span>  
</span></span><span style="display:flex;"><span>writer<span style="color:#666">.</span><span style="color:#4070a0">close</span><span style="color:#666">()</span>
</span></span></code></pre></div><ul>
<li><code>File.newReader(encoding)</code></li>
</ul>
<p>Previously, when creating a new Reader from a file, the encoding was automatically guessed, thanks to CharsetReaderToolkit I had implemented which smartly guessed the charset used to encode the file. Now, it is possible to overide this mechanism:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> reader <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> File<span style="color:#666">(</span><span style="color:#4070a0">&#34;myText.txt&#34;</span><span style="color:#666">).</span><span style="color:#4070a0">newReader</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;UTF-8&#34;</span><span style="color:#666">)</span>
</span></span></code></pre></div><ul>
<li><code>withWriter()</code>:
<ul>
<li><code>File.withWriter(encoding, closure)</code></li>
<li><code>File.withWriterAppend(encoding, closure)</code></li>
<li><code>OutputStream.withWriter(encoding, closure)</code></li>
</ul>
</li>
</ul>
<p>I extended the withWriter methods, so that we may specify an encoding, and also specify the append more:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// no need of course to close the writer,   
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">// since it&#39;s gracefully taken care of by the method  
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>newFile<span style="color:#666">(</span><span style="color:#4070a0">&#34;myText.txt&#34;</span><span style="color:#666">).</span><span style="color:#4070a0">withWriterAppend</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;UTF-8&#34;</span><span style="color:#666">)</span> <span style="color:#666">{</span> writer <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>    writer<span style="color:#666">.</span><span style="color:#4070a0">write</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;\u20AC: euro symbol\n&#34;</span><span style="color:#666">)</span>  
</span></span><span style="display:flex;"><span>    writer<span style="color:#666">.</span><span style="color:#4070a0">write</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;$: dollar symbol\n&#39;</span><span style="color:#666">)</span>  
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>  
</span></span></code></pre></div><ul>
<li><code>BufferedWriter.writeLine(text)</code></li>
</ul>
<p>In the example above, you may also use the new writeLine() method on BufferedReader, which is cleaner than appened &ldquo;\n&rdquo; after each call to write(), so the example becomes:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>newFile<span style="color:#666">(</span><span style="color:#4070a0">&#34;myText.txt&#34;</span><span style="color:#666">).</span><span style="color:#4070a0">withWriterAppend</span><span style="color:#666">(</span><span style="color:#4070a0">&#34;UTF-8&#34;</span><span style="color:#666">)</span> <span style="color:#666">{</span> writer <span style="color:#666">-&gt;</span> 
</span></span><span style="display:flex;"><span>    writer<span style="color:#666">.</span><span style="color:#4070a0">writeLine</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;\u20AC: euro symbol&#39;</span><span style="color:#666">)</span> <span style="color:#60a0b0;font-style:italic">// cleaner than &#34;\n&#34; obviously  
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic"></span>    writer<span style="color:#666">.</span><span style="color:#4070a0">writeLine</span><span style="color:#666">(</span><span style="color:#4070a0">&#39;$: dollar symbol&#39;</span><span style="color:#666">)</span>  
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Note that it appends a platform dependant new line.</p>
<ul>
<li><code>File.readBytes()</code></li>
</ul>
<p>I&rsquo;ve finally changed the readBytes() method to return an array of bytes instead of a List of Bytes which was quite ineficient:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> bytes <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> File<span style="color:#666">(</span><span style="color:#4070a0">&#34;myText.txt&#34;</span><span style="color:#666">).</span><span style="color:#4070a0">readBytes</span><span style="color:#666">()</span>
</span></span></code></pre></div><p>With all these methods, I thing we pretty can do anything we want! We should certainly cover all the scope of IO methods, especially of text files with optional specification of the encoding used.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Timing a closure in Groovy</title><link>https://glaforge.dev/posts/2004/02/26/timing-a-closure-in-groovy/</link><pubDate>Thu, 26 Feb 2004 00:02:00 +0100</pubDate><guid>https://glaforge.dev/posts/2004/02/26/timing-a-closure-in-groovy/</guid><description>&lt;p>When you want to make some optimizations to your code, you often use the good old System.currentTimeMillis() method to time certain parts of your application. I wanted to do a similar thing when hacking some Groovy scripts recently, and the idea came to me that I could simply create a timing closure! So here it is, for your eyes only:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-groovy" data-lang="groovy">&lt;span style="display:flex;">&lt;span>timer &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#666">{&lt;/span> closure &lt;span style="color:#666">-&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> start &lt;span style="color:#666">=&lt;/span> System&lt;span style="color:#666">.&lt;/span>&lt;span style="color:#4070a0">currentTimeMillis&lt;/span>&lt;span style="color:#666">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> closure&lt;span style="color:#666">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> println System&lt;span style="color:#666">.&lt;/span>&lt;span style="color:#4070a0">currentTimeMillis&lt;/span>&lt;span style="color:#666">()&lt;/span> &lt;span style="color:#666">-&lt;/span> start
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then you can use your brand new timing closure:&lt;/p></description><content:encoded>
<![CDATA[<p>When you want to make some optimizations to your code, you often use the good old System.currentTimeMillis() method to time certain parts of your application. I wanted to do a similar thing when hacking some Groovy scripts recently, and the idea came to me that I could simply create a timing closure! So here it is, for your eyes only:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>timer <span style="color:#666">=</span> <span style="color:#666">{</span> closure <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>    start <span style="color:#666">=</span> System<span style="color:#666">.</span><span style="color:#4070a0">currentTimeMillis</span><span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    closure<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    println System<span style="color:#666">.</span><span style="color:#4070a0">currentTimeMillis</span><span style="color:#666">()</span> <span style="color:#666">-</span> start
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>Then you can use your brand new timing closure:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>timer <span style="color:#666">{</span> <span style="color:#4070a0">&#34;sleep 10&#34;</span><span style="color:#666">.</span><span style="color:#4070a0">execute</span><span style="color:#666">().</span><span style="color:#4070a0">waitFor</span><span style="color:#666">()</span> <span style="color:#666">}</span>
</span></span></code></pre></div><p>That&rsquo;s not rocket science, but closures are really a powerful feature. Everyday I wish Java had such a feature built-in.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Happy Birthday Codehaus</title><link>https://glaforge.dev/posts/2004/02/26/happy-birthday-codehaus/</link><pubDate>Thu, 26 Feb 2004 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2004/02/26/happy-birthday-codehaus/</guid><description>&lt;p>A year ago, &lt;a href="http://www.codehaus.org/">Codehaus&lt;/a> saw the light of day. And since, it gathered a lot of bright and talented persons working on innovative and very good quality projects. And moreover, those projects have a friendly licence scheme (I&amp;rsquo;m not a GPL/LGPL lover).&lt;/p>
&lt;p>For a few months, I&amp;rsquo;ve been part of the Hausmates, thanks to my efforts in developing some code for &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a>, and I&amp;rsquo;m really proud of beeing part of it.&lt;/p>
&lt;p>I wish a very happy birthday to Codehaus, and to all those who made it happen. Thank you Bob, and thank you James for Groovy ;-)&lt;/p></description><content:encoded>
<![CDATA[<p>A year ago, <a href="http://www.codehaus.org/">Codehaus</a> saw the light of day. And since, it gathered a lot of bright and talented persons working on innovative and very good quality projects. And moreover, those projects have a friendly licence scheme (I&rsquo;m not a GPL/LGPL lover).</p>
<p>For a few months, I&rsquo;ve been part of the Hausmates, thanks to my efforts in developing some code for <a href="http://groovy.codehaus.org/">Groovy</a>, and I&rsquo;m really proud of beeing part of it.</p>
<p>I wish a very happy birthday to Codehaus, and to all those who made it happen. Thank you Bob, and thank you James for Groovy ;-)</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy-JDK doc: Parsing Java with QDox</title><link>https://glaforge.dev/posts/2004/02/05/groovy-jdk-doc-parsing-java-with-qdox/</link><pubDate>Thu, 05 Feb 2004 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2004/02/05/groovy-jdk-doc-parsing-java-with-qdox/</guid><description>&lt;p>Perhaps you noticed recently that there&amp;rsquo;s a new interesting page on Groovy&amp;rsquo;s website ? Well, all pages are interesting of course! But there&amp;rsquo;s a new page describing the &lt;a href="http://groovy.codehaus.org/groovy-jdk.html">Groovy methods&lt;/a> enhancing the core JDK classes.&lt;/p>
&lt;p>In groovy, you have additional methods that you can call on standard Java classes. For instance, you can use the &lt;a href="http://groovy.codehaus.org/groovy-jdk.html#meth33">eachLine()&lt;/a> method on java.io.File. With this method, you&amp;rsquo;ll be able to easily read a text file line after line, and do whatever with this line inside a closure without having to care about things like closing streams. Let&amp;rsquo;s illustrate this with an example :&lt;/p></description><content:encoded>
<![CDATA[<p>Perhaps you noticed recently that there&rsquo;s a new interesting page on Groovy&rsquo;s website ? Well, all pages are interesting of course! But there&rsquo;s a new page describing the <a href="http://groovy.codehaus.org/groovy-jdk.html">Groovy methods</a> enhancing the core JDK classes.</p>
<p>In groovy, you have additional methods that you can call on standard Java classes. For instance, you can use the <a href="http://groovy.codehaus.org/groovy-jdk.html#meth33">eachLine()</a> method on java.io.File. With this method, you&rsquo;ll be able to easily read a text file line after line, and do whatever with this line inside a closure without having to care about things like closing streams. Let&rsquo;s illustrate this with an example :</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> foo <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> File<span style="color:#666">(</span><span style="color:#4070a0">&#34;myTextFile.txt&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>foo<span style="color:#666">.</span><span style="color:#4070a0">eachLine</span><span style="color:#666">{</span> line <span style="color:#666">-&gt;</span> println line <span style="color:#666">}</span>
</span></span></code></pre></div><p>But there are numerous other Groovy methods like the <a href="http://groovy.codehaus.org/groovy-jdk.html#meth146">Collection.map()</a> for mapping all objects of a collection and transform them, or <a href="http://groovy.codehaus.org/groovy-jdk.html#meth172">List.sort()</a> to sort a collection with a special closure, much simpler than a custom comparator. But so far, all these methods were not documented anywhere, and a new Groovy user would have had to look at the source code closely to discover all the methods available. That&rsquo;s why I told <a href="http://radio.weblogs.com/0112098/">James Strachan</a> we could create a simple tool to generate a documentation for all these Groovy methods to help us&hellip; And obviously, James said&hellip; &ldquo;Groovy!&rdquo; And that&rsquo;s what I did, I created such a tool in Groovy, and using a nice little library called QDox.</p>
<p><a href="http://qdox.codehaus.org/">QDox</a> is a really neat and simple library whose founder is <a href="http://joe.truemesh.com/blog/">Joe Walnes</a>, a brilliant <a href="http://thoughtworks.com/">ThoughtWorkers</a> and a <a href="http://www.codehaus.org/">Hausmate</a>. QDox&rsquo;s main and sole function is to parse JavaDoc comments and build a graph of objects representing the comments, and the structure of a Java class with its fields, methods, parameters, etc&hellip; And it does the job pretty well.</p>
<p>I created a Groovy class called <a href="http://cvs.groovy.codehaus.org/groovy/groovy-core/src/main/org/codehaus/groovy/tools/DocGenerator.groovy?rev=1.15&amp;view=auto">DocGenerator.groovy</a> which parses the <a href="http://cvs.groovy.codehaus.org/groovy/groovy-core/src/main/org/codehaus/groovy/runtime/DefaultGroovyMethods.java?rev=1.108&amp;view=auto">DefaultGroovyMethods</a> class containing all the Groovy methods. I created a JavaDocBuilder, added a source (a Reader on the class to parse), retrieved all the public static methods, then created some <a href="http://groovy.codehaus.org/groovy-jdk.html">HTML output</a>. Pretty straightforward and efficient. Thanks for this nice library.</p>
<p>In the next revision of this DocGenerator tool, I&rsquo;m going to implement different outputs, especially a framed version (with a JavaDoc look&rsquo;n feel), and an XML version which then could be used by the <a href="http://wiki.codehaus.org/groovy/HackingEclipsePlugin">Groovy Eclipse plugin</a> as a source information for code-completion.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>CVS and SSH2, not so easy</title><link>https://glaforge.dev/posts/2004/01/13/cvs-and-ssh2-not-so-easy/</link><pubDate>Tue, 13 Jan 2004 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2004/01/13/cvs-and-ssh2-not-so-easy/</guid><description>&lt;p>Yesterday, I was granted commit rights on the &lt;a href="http://groovy.codehaus.org/">Groovy&lt;/a> source tree, at &lt;a href="http://www.codehaus.org/">Codehaus&lt;/a>. Of special interest for me :&lt;/p>
&lt;ul>
&lt;li>I developed a utility class which helps Groovy create new Readers for text files with the correct encoding already set (I&amp;rsquo;m really keen on charset/encodings issues),&lt;/li>
&lt;li>Adding some new Groovy methods to the core JDK classes,&lt;/li>
&lt;li>Working on an automatic documentation generation engine &amp;ldquo;a la&amp;rdquo; Javadoc, so that it may be possible to browse all the methods that have been added to the core JDK classes.&lt;/li>
&lt;/ul>
&lt;p>James asked me if I wanted a CVS access to deal with those matters of interest instead of tunneling through him, and I gladfully accepted. But, alas, here starts the nightmare&amp;hellip;&lt;/p></description><content:encoded>
<![CDATA[<p>Yesterday, I was granted commit rights on the <a href="http://groovy.codehaus.org/">Groovy</a> source tree, at <a href="http://www.codehaus.org/">Codehaus</a>. Of special interest for me :</p>
<ul>
<li>I developed a utility class which helps Groovy create new Readers for text files with the correct encoding already set (I&rsquo;m really keen on charset/encodings issues),</li>
<li>Adding some new Groovy methods to the core JDK classes,</li>
<li>Working on an automatic documentation generation engine &ldquo;a la&rdquo; Javadoc, so that it may be possible to browse all the methods that have been added to the core JDK classes.</li>
</ul>
<p>James asked me if I wanted a CVS access to deal with those matters of interest instead of tunneling through him, and I gladfully accepted. But, alas, here starts the nightmare&hellip;</p>
<p>So far, I was a happy Windows user, and a happy IntelliJ user, but I&rsquo;m starting to change my mind. It seems that IntelliJ does not speak SSH2. It&rsquo;s clever enough to deal with SSH1, but not SSH2 so far (as far as I know, it that&rsquo;s not the case, please, please tell me how that works!) Thus, I&rsquo;ve had to do everything through the good ol&rsquo;command line. I&rsquo;ve set up a <a href="http://wiki.codehaus.org/general/HowToDevelopWithCygwinUnderWindows">page on Codehaus</a> about that. Read on the steps to follow that I have reproduced here&hellip;</p>
<ol>
<li>First of all, you have to install CYGWIN on your platform: download it here and don&rsquo;t forget to check the options OpenSSH (will be needed for key pair generation) and CVS (you know how useful it may be), let&rsquo;s assume you&rsquo;ve installed it in c:\cygwin which is the default installation directory</li>
<li>Add c:\cygwin\bin to your system path</li>
<li>Open up a bash terminal and type ssh-keygen -t dsa to create an SSH key pair with the default name id_dsa and id_dsa.pub, and don&rsquo;t forget your pass phrase, otherwise your keys will be useless, (put both keys in c:\cygwin\home\jsmith\.ssh)</li>
<li>Give Bob the despot (or the person responsible for the task) your public key (id_dsa.pub) so that he installs it on the CVS server</li>
<li>Once Bob tells you your account is created and your key installed, type eval `ssh-agent` in a terminal (it will tell you it&rsquo;s running in background with pid xxx)</li>
<li>Type ssh-add to add your SSH key to the ssh-agent you&rsquo;ve just launched to take care of the SSH connection and authentication</li>
<li>In order to test your connection, you can follow this optional step : type ssh <a href="mailto:jsmith@cvs.codehaus.org">jsmith@cvs.codehaus.org</a> to test if the connection succeeds then logout. If it fails, you&rsquo;re in trouble! Review all the steps carefully! eventually, export CVS_RSH=ssh to tell CVS that you&rsquo;re using CVS through SSH</li>
<li>You&rsquo;re done, now use cvs commands like cvs -d :ext:jsmith@cvs.codehaus.org:/scm/cvs -co groovy to checkout the latest flavour of groovy (or whatever other project)</li>
</ol>
<p>So now, you can call yourself a Codehaus commiter</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>IntelliJ prayer</title><link>https://glaforge.dev/posts/2004/01/05/intellij-prayer/</link><pubDate>Mon, 05 Jan 2004 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2004/01/05/intellij-prayer/</guid><description>&lt;p>That&amp;rsquo;s incredible, but Robert S. Sfeir is at it again in the &lt;a href="http://www.intellij.net/forums/thread.jsp?forum=22&amp;amp;thread=63779&amp;amp;tstart=0&amp;amp;trange=15#760113">EAP forum&lt;/a>. He produced one more prayer that we should all be repeating again and again. (I hope he won&amp;rsquo;t mind me from quoting his holy words)&lt;/p>
&lt;blockquote>
&lt;p>Our IDEA who art in heaven&lt;br />
Hollowed be thy name&lt;br />
Thy IDE come, the EAP done&lt;br />
On earth as it is in heaven,&lt;br />
Give us this day our daily build,&lt;br />
And forgive our bug reports,&lt;br />
As we forgive those who report against us,&lt;br />
But lead us not into frustration,&lt;br />
Now and forever&amp;hellip;&lt;/p></description><content:encoded>
<![CDATA[<p>That&rsquo;s incredible, but Robert S. Sfeir is at it again in the <a href="http://www.intellij.net/forums/thread.jsp?forum=22&amp;thread=63779&amp;tstart=0&amp;trange=15#760113">EAP forum</a>. He produced one more prayer that we should all be repeating again and again. (I hope he won&rsquo;t mind me from quoting his holy words)</p>
<blockquote>
<p>Our IDEA who art in heaven<br />
Hollowed be thy name<br />
Thy IDE come, the EAP done<br />
On earth as it is in heaven,<br />
Give us this day our daily build,<br />
And forgive our bug reports,<br />
As we forgive those who report against us,<br />
But lead us not into frustration,<br />
Now and forever&hellip;</p></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>IntelliJ version of Miranda Rights</title><link>https://glaforge.dev/posts/2003/12/29/intellij-version-of-miranda-rights/</link><pubDate>Mon, 29 Dec 2003 01:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2003/12/29/intellij-version-of-miranda-rights/</guid><description>&lt;p>After my suggestion of Miranda as a code name for the upcoming EAP of IntelliJ IDEA, there was a &lt;a href="http://www.intellij.net/forums/thread.jsp?forum=22&amp;amp;thread=63081#659938">funny quote from Robert S. Sfeir&lt;/a> in the forums regarding this suggestion. It&amp;rsquo;s a new version of the Miranda Rights :&lt;/p>
&lt;blockquote>
&lt;p>You have the right to refactor. Anything you refactor can be used against you in your code and calls. You have the right to have IDEA present now and during any future refactorings. If you cannot afford an IDEA license, one will be appointed to you, free of charge, during EAP, if you wish.&lt;/p></description><content:encoded>
<![CDATA[<p>After my suggestion of Miranda as a code name for the upcoming EAP of IntelliJ IDEA, there was a <a href="http://www.intellij.net/forums/thread.jsp?forum=22&amp;thread=63081#659938">funny quote from Robert S. Sfeir</a> in the forums regarding this suggestion. It&rsquo;s a new version of the Miranda Rights :</p>
<blockquote>
<p>You have the right to refactor. Anything you refactor can be used against you in your code and calls. You have the right to have IDEA present now and during any future refactorings. If you cannot afford an IDEA license, one will be appointed to you, free of charge, during EAP, if you wish.</p></blockquote>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Code name for IntelliJ IDEA 5.0</title><link>https://glaforge.dev/posts/2003/12/29/code-name-for-intellij-idea-5-0/</link><pubDate>Mon, 29 Dec 2003 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2003/12/29/code-name-for-intellij-idea-5-0/</guid><description>&lt;p>&lt;a href="http://www.intellij.com/">IntelliJ IDEA 4.0&lt;/a> is not yet out of its Early Access Program phase that a &lt;a href="http://www.intellij.net/forums/thread.jsp?forum=22&amp;amp;thread=63081">discussion&lt;/a> already started in the forums for the code name of the next EAP cycle.&lt;/p>
&lt;p>There are already a few propositions for the next code name. Among them:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>StarGate&lt;/strong> (hmm, not that good)&lt;/li>
&lt;li>&lt;strong>Avani&lt;/strong> (which means earth in some indian languages)&lt;/li>
&lt;li>&lt;strong>Stella&lt;/strong> (which will not be used because it was already used for IDEA 2.0 for those who remember)&lt;/li>
&lt;li>&lt;strong>Avalon&lt;/strong> (already used by Microsoft&amp;rsquo;s new UI framework, and the Apache Avalon framework)&lt;/li>
&lt;li>&lt;strong>Rhea&lt;/strong> (suggested internally at JetBrains by &lt;a href="http://www.eugenebelyaev.com/begblog/">Eugene Belyaev&lt;/a>)&lt;/li>
&lt;li>&lt;strong>Aardvark&lt;/strong> (maybe too harsh to pronounce)&lt;/li>
&lt;li>&lt;strong>Atlantis&lt;/strong> (mythological sunken island)&lt;/li>
&lt;li>&lt;strong>Athena&lt;/strong> (greek goddess)&lt;/li>
&lt;li>&lt;strong>Lachesis&lt;/strong>, &lt;strong>Clotho&lt;/strong> or &lt;strong>Athropos&lt;/strong> (the three moirae)&lt;/li>
&lt;li>&lt;strong>Ananke&lt;/strong> (necessity), &lt;strong>Erebus&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Vedra&lt;/strong> (as in Vedra Valles)&lt;/li>
&lt;li>&lt;strong>Miranda&lt;/strong> (that&amp;rsquo;s my suggestion, already used by Miranda-IM, and it &lt;a href="http://glaforge.free.fr/weblog/index.php?itemid=58">reminds something to outlaws in the US&lt;/a>)&lt;/li>
&lt;li>&lt;strong>Olesya&lt;/strong> (CVS integration developer), &lt;strong>Katja&lt;/strong> or &lt;strong>Laika&lt;/strong> (first female dog in space)&lt;/li>
&lt;li>Derivations of Simple (&lt;strong>Simplex&lt;/strong>, &lt;strong>Simplicity&lt;/strong>, &lt;strong>SimpleA&lt;/strong>&amp;hellip;)&lt;/li>
&lt;li>&lt;strong>Dione&lt;/strong> (another Greek goddess)&lt;/li>
&lt;li>&lt;strong>Aphrodite&lt;/strong>, &lt;strong>Athena&lt;/strong>, &lt;strong>Adonis&lt;/strong> (to stick with &amp;ldquo;A&amp;rdquo; and the Greek mythology)&lt;/li>
&lt;li>&lt;strong>Artemida&lt;/strong> (tiger hunter&amp;hellip; JDK 1.5 hunter)&lt;/li>
&lt;li>&lt;strong>Archimedes&lt;/strong> (another sinking idea&amp;hellip;)&lt;/li>
&lt;li>&lt;strong>Revolution&lt;/strong> (somewhat related to the Aurora cruiser)&lt;/li>
&lt;li>&lt;strong>Banana&lt;/strong> (quite funny one)&lt;/li>
&lt;li>&lt;strong>Donut&lt;/strong> (for hungry developers)&lt;/li>
&lt;/ul>
&lt;p>I like Miranda, of course, because that&amp;rsquo;s my suggestion, but I also like Rhea and Ananke, they sound good to my ears. What would you suggest ?&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://www.intellij.com/">IntelliJ IDEA 4.0</a> is not yet out of its Early Access Program phase that a <a href="http://www.intellij.net/forums/thread.jsp?forum=22&amp;thread=63081">discussion</a> already started in the forums for the code name of the next EAP cycle.</p>
<p>There are already a few propositions for the next code name. Among them:</p>
<ul>
<li><strong>StarGate</strong> (hmm, not that good)</li>
<li><strong>Avani</strong> (which means earth in some indian languages)</li>
<li><strong>Stella</strong> (which will not be used because it was already used for IDEA 2.0 for those who remember)</li>
<li><strong>Avalon</strong> (already used by Microsoft&rsquo;s new UI framework, and the Apache Avalon framework)</li>
<li><strong>Rhea</strong> (suggested internally at JetBrains by <a href="http://www.eugenebelyaev.com/begblog/">Eugene Belyaev</a>)</li>
<li><strong>Aardvark</strong> (maybe too harsh to pronounce)</li>
<li><strong>Atlantis</strong> (mythological sunken island)</li>
<li><strong>Athena</strong> (greek goddess)</li>
<li><strong>Lachesis</strong>, <strong>Clotho</strong> or <strong>Athropos</strong> (the three moirae)</li>
<li><strong>Ananke</strong> (necessity), <strong>Erebus</strong></li>
<li><strong>Vedra</strong> (as in Vedra Valles)</li>
<li><strong>Miranda</strong> (that&rsquo;s my suggestion, already used by Miranda-IM, and it <a href="http://glaforge.free.fr/weblog/index.php?itemid=58">reminds something to outlaws in the US</a>)</li>
<li><strong>Olesya</strong> (CVS integration developer), <strong>Katja</strong> or <strong>Laika</strong> (first female dog in space)</li>
<li>Derivations of Simple (<strong>Simplex</strong>, <strong>Simplicity</strong>, <strong>SimpleA</strong>&hellip;)</li>
<li><strong>Dione</strong> (another Greek goddess)</li>
<li><strong>Aphrodite</strong>, <strong>Athena</strong>, <strong>Adonis</strong> (to stick with &ldquo;A&rdquo; and the Greek mythology)</li>
<li><strong>Artemida</strong> (tiger hunter&hellip; JDK 1.5 hunter)</li>
<li><strong>Archimedes</strong> (another sinking idea&hellip;)</li>
<li><strong>Revolution</strong> (somewhat related to the Aurora cruiser)</li>
<li><strong>Banana</strong> (quite funny one)</li>
<li><strong>Donut</strong> (for hungry developers)</li>
</ul>
<p>I like Miranda, of course, because that&rsquo;s my suggestion, but I also like Rhea and Ananke, they sound good to my ears. What would you suggest ?</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Groovy: a sample script</title><link>https://glaforge.dev/posts/2003/12/16/groovy-a-sample-script/</link><pubDate>Tue, 16 Dec 2003 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2003/12/16/groovy-a-sample-script/</guid><description>&lt;p>In the &lt;a href="http://www.intellij.net/forums/">IntelliJ forums&lt;/a>, I came across an off-topic (but funny) &lt;a href="http://www.intellij.net/forums/thread.jsp?forum=22&amp;amp;thread=58983&amp;amp;message=645130&amp;amp;q=536f6d65626f647920746f6c64206d65206f6e6365207468617420746865726520617265206f6e6c792074776f20776f72647320696e2074686520456e676c69736820#645130">post&lt;/a> by Robert Gibson who was wondering :&lt;/p>
&lt;blockquote>
&lt;p>Somebody told me once that there are only two words in the English language which contain each vowel, once only, in alphabetical order. Anybody know what the other one is?&lt;/p>&lt;/blockquote>
&lt;p>Indeed, there are more than two words corresponding to those constraints. I then wrote a little Java class which took all the words of a &lt;a href="http://www.und.edu/org/crypto/crypto/words/english_word_list/">words file&lt;/a> (with 100k words) and tested if they matched a regexp corresponding to those constraints.&lt;/p></description><content:encoded>
<![CDATA[<p>In the <a href="http://www.intellij.net/forums/">IntelliJ forums</a>, I came across an off-topic (but funny) <a href="http://www.intellij.net/forums/thread.jsp?forum=22&amp;thread=58983&amp;message=645130&amp;q=536f6d65626f647920746f6c64206d65206f6e6365207468617420746865726520617265206f6e6c792074776f20776f72647320696e2074686520456e676c69736820#645130">post</a> by Robert Gibson who was wondering :</p>
<blockquote>
<p>Somebody told me once that there are only two words in the English language which contain each vowel, once only, in alphabetical order. Anybody know what the other one is?</p></blockquote>
<p>Indeed, there are more than two words corresponding to those constraints. I then wrote a little Java class which took all the words of a <a href="http://www.und.edu/org/crypto/crypto/words/english_word_list/">words file</a> (with 100k words) and tested if they matched a regexp corresponding to those constraints.</p>
<p>But for the fun and for living on the cutting edge, I decided to also write a <a href="http://groovy-lang.org/">Groovy</a> script doing the very same thing. In fact, it&rsquo;s much more consise, and overall, I&rsquo;ve fallen in love with closures. I&rsquo;d love to have closures in Java.</p>
<p>Here is my little script (in fact, it is my second ever written Groovy script after my <a href="http://glaforge.free.fr/weblog/index.php?itemid=46&amp;catid=2">LOAF implementation</a>)</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#902000">def</span> foo <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> File<span style="color:#666">(</span><span style="color:#4070a0">&#34;wordlist.txt&#34;</span><span style="color:#666">)</span>
</span></span><span style="display:flex;"><span>foo<span style="color:#666">.</span><span style="color:#4070a0">eachLine</span><span style="color:#666">{</span> word <span style="color:#666">-&gt;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#007020;font-weight:bold">if</span> <span style="color:#666">(</span>word <span style="color:#666">==~</span> <span style="color:#4070a0">&#34;^[^aeiou]*a[^aeiou]*e[^aeiou]*i[^aeiou]*o[^aeiou]*u[^aeiou]*$&#34;</span> <span style="color:#666">)</span> 
</span></span><span style="display:flex;"><span>    println word
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Some good rules for an efficient ant build script</title><link>https://glaforge.dev/posts/2003/12/08/some-good-rules-for-an-efficient-ant-build-script/</link><pubDate>Mon, 08 Dec 2003 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2003/12/08/some-good-rules-for-an-efficient-ant-build-script/</guid><description>&lt;p>Some time ago I came across an interesting article on Apache&amp;rsquo;s wiki entitled (&lt;a href="http://nagoya.apache.org/wiki/apachewiki.cgi?TheElementsOfAntStyle">Apache Wiki: The Elements Of Ant Style&lt;/a>. It&amp;rsquo;s a very thourough overview of good practices for writing clean, readable, reusable and efficient Ant build files.&lt;/p>
&lt;p>Today, through &lt;a href="http://www.magpiebrain.com/archives/000151.html">magpiebrain&lt;/a>, I read on &lt;a href="http://www.onjava.com/">OnJava&lt;/a> another very interesting and concise article giving &lt;a href="http://www.onjava.com/pub/a/onjava/2003/12/17/ant_bestpractices.html">15 Ant best practices&lt;/a>. It&amp;rsquo;s much shorter than the previous article on Apache&amp;rsquo;s wiki, but still, with those 15 practices you&amp;rsquo;ll manage to improve some messy build scripts. Believe me !&lt;/p></description><content:encoded>
<![CDATA[<p>Some time ago I came across an interesting article on Apache&rsquo;s wiki entitled (<a href="http://nagoya.apache.org/wiki/apachewiki.cgi?TheElementsOfAntStyle">Apache Wiki: The Elements Of Ant Style</a>. It&rsquo;s a very thourough overview of good practices for writing clean, readable, reusable and efficient Ant build files.</p>
<p>Today, through <a href="http://www.magpiebrain.com/archives/000151.html">magpiebrain</a>, I read on <a href="http://www.onjava.com/">OnJava</a> another very interesting and concise article giving <a href="http://www.onjava.com/pub/a/onjava/2003/12/17/ant_bestpractices.html">15 Ant best practices</a>. It&rsquo;s much shorter than the previous article on Apache&rsquo;s wiki, but still, with those 15 practices you&rsquo;ll manage to improve some messy build scripts. Believe me !</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Une gouache de macareux</title><link>https://glaforge.dev/posts/2003/12/07/une-gouache-de-macareux/</link><pubDate>Sun, 07 Dec 2003 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2003/12/07/une-gouache-de-macareux/</guid><description>&lt;p>Voici un nouveau tableau que j&amp;rsquo;ai réalisé à la gouache. Il s&amp;rsquo;agit de trois petits macareux, que j&amp;rsquo;ai peints pour offrir en guise de cadeau de Noël pour ma mère&amp;hellip; Encore une chance qu&amp;rsquo;elle n&amp;rsquo;a pas internet chez elle, sinon, elle pourrait découvrir la surprise avant l&amp;rsquo;heure !&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-bcb78f94afba84d8d998112616280e64">
&lt;img src="https://glaforge.dev/img/misc/Macareux.jpg"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-bcb78f94afba84d8d998112616280e64">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/Macareux.jpg"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p></description><content:encoded>
<![CDATA[<p>Voici un nouveau tableau que j&rsquo;ai réalisé à la gouache. Il s&rsquo;agit de trois petits macareux, que j&rsquo;ai peints pour offrir en guise de cadeau de Noël pour ma mère&hellip; Encore une chance qu&rsquo;elle n&rsquo;a pas internet chez elle, sinon, elle pourrait découvrir la surprise avant l&rsquo;heure !</p>
<p><figure>
  <a href="#img-bcb78f94afba84d8d998112616280e64">
    <img src="/img/misc/Macareux.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-bcb78f94afba84d8d998112616280e64">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/Macareux.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>LOAF : a Groovy implementation</title><link>https://glaforge.dev/posts/2003/11/14/loaf-a-groovy-implementation/</link><pubDate>Fri, 14 Nov 2003 00:00:00 +0100</pubDate><guid>https://glaforge.dev/posts/2003/11/14/loaf-a-groovy-implementation/</guid><description>&lt;p>&lt;a href="http://postneo.com/postwiki/moin.cgi/LoafHome">LOAF&lt;/a> is taking the community by storm. I do firmly believe it gonna rock the world. Social software is the way to go : the next revolution. But as far as I know there were no LOAF implementation in &lt;a href="http://groovy-lang.org">Groovy&lt;/a>, a promising programming language.&lt;/p>
&lt;p>So let&amp;rsquo;s see how simple it is to write a LOAF implementation using Groovy:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-groovy" data-lang="groovy">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">class&lt;/span> &lt;span style="color:#0e84b5;font-weight:bold">LOAF&lt;/span> &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">static&lt;/span> &lt;span style="color:#06287e">main&lt;/span>&lt;span style="color:#666">(&lt;/span>args&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#902000">def&lt;/span> loaf &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020;font-weight:bold">new&lt;/span> LOAF&lt;span style="color:#666">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I&amp;rsquo;m a beginner in Groovy, so use at your own risk. Play with it, it&amp;rsquo;s free as in beer !&lt;/p></description><content:encoded>
<![CDATA[<p><a href="http://postneo.com/postwiki/moin.cgi/LoafHome">LOAF</a> is taking the community by storm. I do firmly believe it gonna rock the world. Social software is the way to go : the next revolution. But as far as I know there were no LOAF implementation in <a href="http://groovy-lang.org">Groovy</a>, a promising programming language.</p>
<p>So let&rsquo;s see how simple it is to write a LOAF implementation using Groovy:</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">LOAF</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#007020;font-weight:bold">static</span> <span style="color:#06287e">main</span><span style="color:#666">(</span>args<span style="color:#666">)</span> <span style="color:#666">{</span>
</span></span><span style="display:flex;"><span>        <span style="color:#902000">def</span> loaf <span style="color:#666">=</span> <span style="color:#007020;font-weight:bold">new</span> LOAF<span style="color:#666">()</span>
</span></span><span style="display:flex;"><span>    <span style="color:#666">}</span>
</span></span><span style="display:flex;"><span><span style="color:#666">}</span>
</span></span></code></pre></div><p>I&rsquo;m a beginner in Groovy, so use at your own risk. Play with it, it&rsquo;s free as in beer !</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Wiki-like Java HTML editor !</title><link>https://glaforge.dev/posts/2003/10/23/wiki-like-java-html-editor/</link><pubDate>Thu, 23 Oct 2003 01:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2003/10/23/wiki-like-java-html-editor/</guid><description>&lt;p>Well, sometimes, we happend to make some fun stuff at work. Don&amp;rsquo;t we ? Currently, we&amp;rsquo;re developping a RAD tool for creating applications using a framework we&amp;rsquo;ve been developping for some time already. It&amp;rsquo;s a classical Swing app. We decided to create a contextual help system : Each time a component is selected in the GUI (whether it be a tabbed pane or a node in a JTree), we show the corresponding help page in another frame that the user may show or hide as he wishes when he needs some help. I use a JEditorPane with an HTMLEditorKit to show those HTML help pages.&lt;/p></description><content:encoded>
<![CDATA[<p>Well, sometimes, we happend to make some fun stuff at work. Don&rsquo;t we ? Currently, we&rsquo;re developping a RAD tool for creating applications using a framework we&rsquo;ve been developping for some time already. It&rsquo;s a classical Swing app. We decided to create a contextual help system : Each time a component is selected in the GUI (whether it be a tabbed pane or a node in a JTree), we show the corresponding help page in another frame that the user may show or hide as he wishes when he needs some help. I use a JEditorPane with an HTMLEditorKit to show those HTML help pages.</p>
<p>Well, you&rsquo;ll tell me there&rsquo;s nothing fun there, of course. The interesting part comes from the fact that I developped a wiki-like system for creating the content of those help pages. At work, nobody like writing documentation unfortunately, but with this new tool, you just check the edit checkbox, and you can WYSIWYG-ly edit your HTML page : create headings, paragraphs, styles, lists, etc. You can insert images, and create links to other pages. When creating a link, you have the choice of linking to an existing page, or you can automagically create a new page ! Indeed, I created a wiki-like system for creating, editing and viewing HTML help pages in our app.</p>
<p>Another idea we&rsquo;ve had is to allow the final users to be able to append some user-defined comments on the help pages of our app. I think it&rsquo;s a rather innovative idea to be able to edit yourself the help system.</p>
<p>If I have some time, maybe I&rsquo;ll turn that into a nice IntelliJ IDEA plugin, so that one may edit some simple HTML pages, for storing some developper documentation.</p>
<p>In a future post, I&rsquo;ll give you something to play with. So stay tuned !</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Halloween Jukebox</title><link>https://glaforge.dev/posts/2003/10/23/halloween-jukebox/</link><pubDate>Thu, 23 Oct 2003 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2003/10/23/halloween-jukebox/</guid><description>&lt;p>It&amp;rsquo;s soon Halloween ! If you&amp;rsquo;re making a party at home, like my girl friend and me, you&amp;rsquo;ll want to frighten your friends a little bit. Some make-up, some fancy closes, some pumpkins, etc&amp;hellip; and some&amp;hellip; hhoorrriibblleee soooouuunnnndddsss !&lt;/p>
&lt;p>I found on the net some nice wave sounds like evil laugthers, screams, ghosts noises, etc. Stéphanie wanted to make a Windows Media Player playlist with those sounds. Cool&amp;hellip; But as a Java Geek, I decided to make something better ! That&amp;rsquo;s why I decided to write a little Halloween jukebox.&lt;/p></description><content:encoded>
<![CDATA[<p>It&rsquo;s soon Halloween ! If you&rsquo;re making a party at home, like my girl friend and me, you&rsquo;ll want to frighten your friends a little bit. Some make-up, some fancy closes, some pumpkins, etc&hellip; and some&hellip; hhoorrriibblleee soooouuunnnndddsss !</p>
<p>I found on the net some nice wave sounds like evil laugthers, screams, ghosts noises, etc. Stéphanie wanted to make a Windows Media Player playlist with those sounds. Cool&hellip; But as a Java Geek, I decided to make something better ! That&rsquo;s why I decided to write a little Halloween jukebox.</p>
<p>The aim of this jukebox is to play random sounds from selected sub-directories (representing a category) containing sounds, at defined interval of time.</p>
<p>You will find the jar here : <a href="http://glaforge.free.fr/projects/halloweenjukebox/HalloweenJukebox.jar">HalloweenJukebox.jar</a>, and you will also have to download a zip with a few sounds there <a href="http://glaforge.free.fr/projects/halloweenjukebox/sounds.zip">sounds.zip</a>.</p>
<p><strong>Instructions</strong> : save the jar file on your computer, save the zip file as well and unpack it somewhere. Double-click on the jar file to launch the jukebox. Select the &ldquo;sound&rdquo; directory where you have unzipped sounds.zip.</p>
<p><strong>Explanations</strong> : in the GUI, you&rsquo;ll see on the left a list of sub-directories checked, that&rsquo;s the sounds you&rsquo;ve chosen to play. Below you&rsquo;ll recognize the play and stop button. And you will also be able to select the time interval between each sound. Thanks to javax.sound, all sounds will be mixed in real-time, overlapping each other if you&rsquo;ve chosen a short interval, it&rsquo;ll make some terrific soundtrack for your party ;-)</p>
<p>Enjoy, and Happy Halloween !</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>IntelliJ IDEA : ShowEncoding plugin</title><link>https://glaforge.dev/posts/2003/10/20/intellij-idea-showencoding-plugin/</link><pubDate>Mon, 20 Oct 2003 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2003/10/20/intellij-idea-showencoding-plugin/</guid><description>&lt;p>A bit more than a year ago, I wrote some utility classes related to file encodings/charsets. Those classes got integrated to IntelliJ IDEA. Those classes are useful for knowing the encoding of a byte array, or a file, or an input stream. You&amp;rsquo;ll be able to know whether your file is encoded using UTF-8, or whether it used ISO-8859-1, or windows&amp;rsquo; specific windows-1252.&lt;/p>
&lt;p>Unfortunately, inside IntelliJ IDEA, it is not currently possible to know the charset of the files edited. I filed a feature request in june. Unfortunately, this feature has not yet been implemented.&lt;/p></description><content:encoded>
<![CDATA[<p>A bit more than a year ago, I wrote some utility classes related to file encodings/charsets. Those classes got integrated to IntelliJ IDEA. Those classes are useful for knowing the encoding of a byte array, or a file, or an input stream. You&rsquo;ll be able to know whether your file is encoded using UTF-8, or whether it used ISO-8859-1, or windows&rsquo; specific windows-1252.</p>
<p>Unfortunately, inside IntelliJ IDEA, it is not currently possible to know the charset of the files edited. I filed a feature request in june. Unfortunately, this feature has not yet been implemented.</p>
<p>That&rsquo;s why I decided to write my own plugin for discovering the charset used in my files.</p>
<p>For download and more information, you can browse these links :</p>
<ul>
<li><a href="http://www.intellij.org/twiki/bin/view/Main/ShowEncodingPlugin">Project page on the Wiki</a></li>
</ul>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Unicode, charset, encodings</title><link>https://glaforge.dev/posts/2003/10/16/unicode-charset-encodings/</link><pubDate>Thu, 16 Oct 2003 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2003/10/16/unicode-charset-encodings/</guid><description>&lt;p>Today, through Java blogs, I came across &lt;a href="http://www.joelonsoftware.com/articles/Unicode.html">Joel Spolsky&amp;rsquo;s post&lt;/a> regarding &lt;strong>&lt;em>The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets&lt;/em>&lt;/strong>. I have to recommend anybody curious about i18n issues to read this very good introduction. I wish I had read that before, two years ago, when I had to deal with different file encodings ;-)&lt;/p>
&lt;p>This article remind me of one of my old home projects, back in the summer 2002. I was developing (and I&amp;rsquo;m still doing so) with IntelliJ IDEA at work, and I had messed up with some critical XML files because I was playing with the default encoding used by IntelliJ in the preferences panel&amp;hellip; I was so ashamed that I decided to file some feature requests about automatic charset recognition for my beloved Java IDE. And even better, I coded some java classes that I gave Maxim Shafirov. And my code was finally integrated (perhaps modified to suit their needs) into IDEA 3.0.&lt;/p></description><content:encoded>
<![CDATA[<p>Today, through Java blogs, I came across <a href="http://www.joelonsoftware.com/articles/Unicode.html">Joel Spolsky&rsquo;s post</a> regarding <strong><em>The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets</em></strong>. I have to recommend anybody curious about i18n issues to read this very good introduction. I wish I had read that before, two years ago, when I had to deal with different file encodings ;-)</p>
<p>This article remind me of one of my old home projects, back in the summer 2002. I was developing (and I&rsquo;m still doing so) with IntelliJ IDEA at work, and I had messed up with some critical XML files because I was playing with the default encoding used by IntelliJ in the preferences panel&hellip; I was so ashamed that I decided to file some feature requests about automatic charset recognition for my beloved Java IDE. And even better, I coded some java classes that I gave Maxim Shafirov. And my code was finally integrated (perhaps modified to suit their needs) into IDEA 3.0.</p>
<p>For people interested in this package, you can have a look at my project page : <a href="https://github.com/codehaus/guessencoding">GuessEncoding</a>.</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Une esquisse de Renaud</title><link>https://glaforge.dev/posts/2003/07/28/une-esquisse-de-renaud/</link><pubDate>Mon, 28 Jul 2003 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2003/07/28/une-esquisse-de-renaud/</guid><description>&lt;p>Inspiré par Titouan Lamazou, j&amp;rsquo;ai immité le geste du peintre en barbouillant une feuille de Canson, je vous laisse admirer le résultat.&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-8135a0ad0106fcdc65d010d60513650d">
&lt;img src="https://glaforge.dev/img/misc/renaud.jpg"
alt=""
/>
&lt;/a>
&lt;figcaption>&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-8135a0ad0106fcdc65d010d60513650d">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/renaud.jpg"
alt=""
/>
&lt;div class="lightbox-caption">&lt;/div>
&lt;/div>
&lt;/p></description><content:encoded>
<![CDATA[<p>Inspiré par Titouan Lamazou, j&rsquo;ai immité le geste du peintre en barbouillant une feuille de Canson, je vous laisse admirer le résultat.</p>
<p><figure>
  <a href="#img-8135a0ad0106fcdc65d010d60513650d">
    <img src="/img/misc/renaud.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-8135a0ad0106fcdc65d010d60513650d">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/renaud.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Allergie aux piqûres de taon</title><link>https://glaforge.dev/posts/2003/06/09/allergie-aux-piqures-de-taon/</link><pubDate>Mon, 09 Jun 2003 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2003/06/09/allergie-aux-piqures-de-taon/</guid><description>&lt;p>Les taons sont vraiment de sales bêbêtes. Et le problème le plus embêtant, c&amp;rsquo;est que je suis allergique à leurs piqûres. Alors en Alsace, sur le terrain en pleine campagne où se déroulaient les expériences scientifiques de ma petite-amie, il y en avait à revendre de ces insectes ! Et je me suis fait piquer deux fois : une fois à chaque main. En quelques heures mes mains ont littéralement doublé de volume. A tel point que lors de notre excursion à Europapark en Allemagne, j&amp;rsquo;ai dû me faire bander les mains par un infirmier du point Croix Rouge.&lt;/p></description><content:encoded>
<![CDATA[<p>Les taons sont vraiment de sales bêbêtes. Et le problème le plus embêtant, c&rsquo;est que je suis allergique à leurs piqûres. Alors en Alsace, sur le terrain en pleine campagne où se déroulaient les expériences scientifiques de ma petite-amie, il y en avait à revendre de ces insectes ! Et je me suis fait piquer deux fois : une fois à chaque main. En quelques heures mes mains ont littéralement doublé de volume. A tel point que lors de notre excursion à Europapark en Allemagne, j&rsquo;ai dû me faire bander les mains par un infirmier du point Croix Rouge.</p>
<p>En voici la preuve :</p>
<p><figure>
  <a href="#img-72287e4e6850e198a3e0f254c35502e7">
    <img src="/img/misc/mains-bandees.jpg"
      alt="mains bandées après les piqûres de taon"
       />
  </a>
  <figcaption>mains bandées après les piqûres de taon</figcaption>
</figure>
<div class="lightbox" id="img-72287e4e6850e198a3e0f254c35502e7">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/mains-bandees.jpg"
    alt="mains bandées après les piqûres de taon"
     />
  <div class="lightbox-caption">mains bandées après les piqûres de taon</div>
</div>
</p>
<p>En rentrant sur Strasbourg, nous avons appelé un médecin pour qu&rsquo;il me fasse une petite piqûre (une autre ?) de cortisone (cette fois-ci) et pour me donner des médicaments adéquats. Voici à peu près à quoi ressemblaient mes mains lorsqu&rsquo;il m&rsquo;a retiré mes bandages :</p>
<p><figure>
  <a href="#img-b2039f027b919b7315fb0d321f182690">
    <img src="/img/misc/grosse-main.jpg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-b2039f027b919b7315fb0d321f182690">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/grosse-main.jpg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</p>
<p>Impressionnant, n&rsquo;est-ce pas ?</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Premier tableau : les falaises d'Etretat</title><link>https://glaforge.dev/posts/2003/06/08/premier-tableau-les-falaises-d-etretat/</link><pubDate>Sun, 08 Jun 2003 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2003/06/08/premier-tableau-les-falaises-d-etretat/</guid><description>&lt;p>J&amp;rsquo;ai décidé il y a quelques semaines de me remettre à la peinture, d&amp;rsquo;abord en faisant quelques toiles à la gouache, pour passer bientôt à la peinture à l&amp;rsquo;huile. En guise de cadeau à ma bien-aimée, j&amp;rsquo;ai décidé de peindre les falaises d&amp;rsquo;Etretat, que nous avions eu le plaisir d&amp;rsquo;admirer à la Toussaint 2002.&lt;/p>
&lt;p>Voici donc le résultat de cette première tentative de peinture :&lt;/p>
&lt;p>&lt;figure>
&lt;a href="#img-77270aa5f2a453fa8555cb754dca9c94">
&lt;img src="https://glaforge.dev/img/misc/Etretat.jpg"
alt="Les falaises d&amp;rsquo;Etretat"
/>
&lt;/a>
&lt;figcaption>Les falaises d&amp;rsquo;Etretat&lt;/figcaption>
&lt;/figure>
&lt;div class="lightbox" id="img-77270aa5f2a453fa8555cb754dca9c94">
&lt;a href="#_" class="lightbox-overlay">&lt;/a>
&lt;img src="https://glaforge.dev/img/misc/Etretat.jpg"
alt="Les falaises d&amp;rsquo;Etretat"
/>
&lt;div class="lightbox-caption">Les falaises d&amp;rsquo;Etretat&lt;/div>
&lt;/div>
&lt;/p></description><content:encoded>
<![CDATA[<p>J&rsquo;ai décidé il y a quelques semaines de me remettre à la peinture, d&rsquo;abord en faisant quelques toiles à la gouache, pour passer bientôt à la peinture à l&rsquo;huile. En guise de cadeau à ma bien-aimée, j&rsquo;ai décidé de peindre les falaises d&rsquo;Etretat, que nous avions eu le plaisir d&rsquo;admirer à la Toussaint 2002.</p>
<p>Voici donc le résultat de cette première tentative de peinture :</p>
<p><figure>
  <a href="#img-77270aa5f2a453fa8555cb754dca9c94">
    <img src="/img/misc/Etretat.jpg"
      alt="Les falaises d&rsquo;Etretat"
       />
  </a>
  <figcaption>Les falaises d&rsquo;Etretat</figcaption>
</figure>
<div class="lightbox" id="img-77270aa5f2a453fa8555cb754dca9c94">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/misc/Etretat.jpg"
    alt="Les falaises d&rsquo;Etretat"
     />
  <div class="lightbox-caption">Les falaises d&rsquo;Etretat</div>
</div>
</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Beignets d'acacia</title><link>https://glaforge.dev/posts/2003/05/28/beignets-d-acacia/</link><pubDate>Wed, 28 May 2003 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2003/05/28/beignets-d-acacia/</guid><description>&lt;p>L&amp;rsquo;époque est maintenant révolue, mais à l&amp;rsquo;approche de l&amp;rsquo;été, les acacias fleurissent. Et qui dit acacia, dit&amp;hellip; beignets de fleurs d&amp;rsquo;acacia. Il y a quelques semaines, alléché par l&amp;rsquo;odeur des fleurs de ces arbres qui prennent racines non loin de chez moi, j&amp;rsquo;ai décidé de préparer moi-même de ces délicieux beignets que ma mère sait si bien faire. Histoire également de faire découvrir cette saveur à ma tendre bien aimée.&lt;/p></description><content:encoded>
<![CDATA[<p>L&rsquo;époque est maintenant révolue, mais à l&rsquo;approche de l&rsquo;été, les acacias fleurissent. Et qui dit acacia, dit&hellip; beignets de fleurs d&rsquo;acacia. Il y a quelques semaines, alléché par l&rsquo;odeur des fleurs de ces arbres qui prennent racines non loin de chez moi, j&rsquo;ai décidé de préparer moi-même de ces délicieux beignets que ma mère sait si bien faire. Histoire également de faire découvrir cette saveur à ma tendre bien aimée.</p>
<p>Nous sommes donc allés cueillir des grappes de fleurs, que nous avons ensuite équeutées. Après cela, nous avons préparé la pâte à beignet, puis mélangé avec les fleurs d&rsquo;acacia. Après cela, on forme quelques cuillères de mélange qu&rsquo;on immerge dans la friture, pour en resortir quelques instants après des beignets croustillants qu&rsquo;il ne reste plus qu&rsquo;à sucrer et&hellip; à déguster !</p>
<p>Voici la recette dont je me suis inspiré, que j&rsquo;ai trouvée sur l&rsquo;excellent site de recettes <a href="http://www.marmiton.org/">marmiton.org</a> :</p>
<blockquote>
<p><strong>Préparation :</strong> 15 mn</p>
<p><strong>Ingrédients</strong> (pour 4 gourmands) :</p>
<ul>
<li>500 g de farine</li>
<li>2 oeufs</li>
<li>2 verres de lait</li>
<li>1 pincée de sel</li>
<li>2 cuillères à soupe de sucre</li>
<li>fleurs d&rsquo;acacia (un saladier de taille moyenne)</li>
</ul>
<p><strong>Préparation :</strong></p>
<ul>
<li>Dans un saladier, mélanger la farine, les oeufs, le sel, le sucre et le lait.</li>
<li>Egrainer les fleurs de l&rsquo;accacia de leur grappe, rincer légérement à l&rsquo;eau, égoutter.</li>
<li>Mélanger les fleurs à la pâte à beignets.</li>
<li>Dans une friteuse, faire bouillir de l&rsquo;huile (ou de la végétaline) et y verser des cuillères du mélange.</li>
<li>Quand les beignets sont bien dorés, les retirer avec une écumoire.</li>
<li>Les sucrer légèrement et servir frais.</li>
</ul></blockquote>
<p>Et voila, maintenant, il ne vous reste plus qu&rsquo;à déguster !</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Calcul du jour de Pâques (suite)</title><link>https://glaforge.dev/posts/2003/04/08/calcul-du-jour-de-paques-suite/</link><pubDate>Tue, 08 Apr 2003 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2003/04/08/calcul-du-jour-de-paques-suite/</guid><description>&lt;p>Imaginons que nous souhaitons créer un calendrier. Bien ! Mais pour être encore plus sympathique, nous allons rajouter sur ce calendrier les &lt;strong>jours fériés&lt;/strong>. Ce n&amp;rsquo;est pas un problème pour les dates fixes comme le 1er janvier ou le 11 novembre. Mais il existe d&amp;rsquo;autres jours fériés qui sont dits &amp;ldquo;&lt;strong>mobiles&lt;/strong>&amp;rdquo;. Dans de très nombreux pays occidentaux, la pluspart de ces jours fériés mobiles sont définis par rapport à la date du jour de Pâques. Or, il faut déjà connaître le jour de Pâques. Effectivement, nous allons donc nous intéresser au calcul du jours de Pâques, qui est une fête religieuse dont la date est définie relativement à une phase lunaire proche de l&amp;rsquo;équinoxe de printemps du 21 mars. Je vous épargnerai les détails, mais voici une petite classe que vous pourrez modifier à loisir pour calculer le jours de Pâques.&lt;/p></description><content:encoded>
<![CDATA[<p>Imaginons que nous souhaitons créer un calendrier. Bien ! Mais pour être encore plus sympathique, nous allons rajouter sur ce calendrier les <strong>jours fériés</strong>. Ce n&rsquo;est pas un problème pour les dates fixes comme le 1er janvier ou le 11 novembre. Mais il existe d&rsquo;autres jours fériés qui sont dits &ldquo;<strong>mobiles</strong>&rdquo;. Dans de très nombreux pays occidentaux, la pluspart de ces jours fériés mobiles sont définis par rapport à la date du jour de Pâques. Or, il faut déjà connaître le jour de Pâques. Effectivement, nous allons donc nous intéresser au calcul du jours de Pâques, qui est une fête religieuse dont la date est définie relativement à une phase lunaire proche de l&rsquo;équinoxe de printemps du 21 mars. Je vous épargnerai les détails, mais voici une petite classe que vous pourrez modifier à loisir pour calculer le jours de Pâques.</p>
<p>Je vous épargne les détails de l&rsquo;algorithme, car on peut le trouver sur Internet, mais au moins, vous en aurez une implémentation en Java !</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">EasterDefinition</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span><span style="color:#06287e">alpha</span>(<span style="color:#902000">int</span><span style="color:#bbb"> </span>year)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(year<span style="color:#bbb"> </span><span style="color:#666">&lt;=</span><span style="color:#bbb"> </span>1582)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>0;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(year<span style="color:#bbb"> </span><span style="color:#666">&lt;=</span><span style="color:#bbb"> </span>1699)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>7;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(year<span style="color:#bbb"> </span><span style="color:#666">&lt;=</span><span style="color:#bbb"> </span>1899)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>8;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(year<span style="color:#bbb"> </span><span style="color:#666">&lt;=</span><span style="color:#bbb"> </span>2199)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>9;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>10;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span><span style="color:#06287e">beta</span>(<span style="color:#902000">int</span><span style="color:#bbb"> </span>year)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(year<span style="color:#bbb"> </span><span style="color:#666">&lt;=</span><span style="color:#bbb"> </span>1582)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>0;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(year<span style="color:#bbb"> </span><span style="color:#666">&lt;=</span><span style="color:#bbb"> </span>1699)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>4;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(year<span style="color:#bbb"> </span><span style="color:#666">&lt;=</span><span style="color:#bbb"> </span>1799)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>3;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(year<span style="color:#bbb"> </span><span style="color:#666">&lt;=</span><span style="color:#bbb"> </span>1899)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>2;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(year<span style="color:#bbb"> </span><span style="color:#666">&lt;=</span><span style="color:#bbb"> </span>2099)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>1;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">if</span><span style="color:#bbb"> </span>(year<span style="color:#bbb"> </span><span style="color:#666">&lt;=</span><span style="color:#bbb"> </span>2199)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>0;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">else</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>6;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span><span style="color:#06287e">r1</span>(<span style="color:#902000">int</span><span style="color:#bbb"> </span>year)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>(19<span style="color:#bbb"> </span><span style="color:#666">*</span><span style="color:#bbb"> </span>(year<span style="color:#bbb"> </span><span style="color:#666">%</span><span style="color:#bbb"> </span>19)<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>15<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>alpha(year))<span style="color:#bbb"> </span><span style="color:#666">%</span><span style="color:#bbb"> </span>30;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">/** 
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">     * Nombre de jours entre Pâques et le 21 mars.
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">     *
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">     * @param year l&#39;année dont on souhaite connaître le jour de Pâques.
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">     * @return le nombre de jours.
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">     */</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span><span style="color:#06287e">r</span>(<span style="color:#902000">int</span><span style="color:#bbb"> </span>year)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>r1m<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>r1(year);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>r1m<span style="color:#bbb"> </span><span style="color:#666">&lt;</span><span style="color:#bbb"> </span>28<span style="color:#bbb"> </span><span style="color:#666">?</span><span style="color:#bbb"> </span>r1m<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>(r1m<span style="color:#bbb"> </span><span style="color:#666">==</span><span style="color:#bbb"> </span>28<span style="color:#bbb"> </span><span style="color:#666">?</span><span style="color:#bbb"> </span>(year<span style="color:#bbb"> </span><span style="color:#666">%</span><span style="color:#bbb"> </span>19<span style="color:#bbb"> </span><span style="color:#666">&gt;</span><span style="color:#bbb"> </span>10<span style="color:#bbb"> </span><span style="color:#666">?</span><span style="color:#bbb"> </span>27<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>28)<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>28);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">private</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">int</span><span style="color:#bbb"> </span><span style="color:#06287e">t</span>(<span style="color:#902000">int</span><span style="color:#bbb"> </span>year)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span><span style="color:#007020;font-weight:bold">return</span><span style="color:#bbb"> </span>(2<span style="color:#bbb"> </span><span style="color:#666">*</span><span style="color:#bbb"> </span>(year<span style="color:#bbb"> </span><span style="color:#666">%</span><span style="color:#bbb"> </span>4)<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>4<span style="color:#bbb"> </span><span style="color:#666">*</span><span style="color:#bbb"> </span>(year<span style="color:#bbb"> </span><span style="color:#666">%</span><span style="color:#bbb"> </span>7)<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>6<span style="color:#bbb"> </span><span style="color:#666">*</span><span style="color:#bbb"> </span>r(year)<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>6<span style="color:#bbb"> </span><span style="color:#666">-</span><span style="color:#bbb"> </span>beta(year))<span style="color:#bbb"> </span><span style="color:#666">%</span><span style="color:#bbb"> </span>7;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#60a0b0;font-style:italic">/**
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">     * Calcule le jour de Pâques pour une année donnée.
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">     *
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">     * @param year l&#39;année dont on souhaite connaître le jour de Pâques.
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">     * @return le jour de Pâques.
</span></span></span><span style="display:flex;"><span><span style="color:#60a0b0;font-style:italic">     */</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">     </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">main</span>(String<span style="color:#666">[]</span><span style="color:#bbb"> </span>args)<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">     </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">         </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>year<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>2003;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">         </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>rm<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>r(year);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">         </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>tm<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>t(year);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">         </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>day<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>rm<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>tm<span style="color:#bbb"> </span><span style="color:#666">&lt;=</span><span style="color:#bbb"> </span>9<span style="color:#bbb"> </span><span style="color:#666">?</span><span style="color:#bbb"> </span>rm<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>tm<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>22<span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>rm<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>tm<span style="color:#bbb"> </span><span style="color:#666">-</span><span style="color:#bbb"> </span>9;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">         </span><span style="color:#902000">int</span><span style="color:#bbb"> </span>month<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>rm<span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>tm<span style="color:#bbb"> </span><span style="color:#666">&lt;=</span><span style="color:#bbb"> </span>9<span style="color:#bbb"> </span><span style="color:#666">?</span><span style="color:#bbb"> </span>Calendar.<span style="color:#4070a0">MARCH</span><span style="color:#bbb"> </span>:<span style="color:#bbb"> </span>Calendar.<span style="color:#4070a0">APRIL</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">     </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Calcul du jour de Pâques</title><link>https://glaforge.dev/posts/2003/04/04/calcul-du-jour-de-paques/</link><pubDate>Fri, 04 Apr 2003 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2003/04/04/calcul-du-jour-de-paques/</guid><description>&lt;p>Toujours dans l&amp;rsquo;optique de créer un &lt;strong>Agenda partagé&lt;/strong> pour l&amp;rsquo;application &lt;strong>OpenProspect&lt;/strong>, il me faut mettre sur mon calendrier&amp;hellip; les &lt;strong>jours fériés&lt;/strong> ! Jusque là, tout va bien&amp;hellip; Mais en cherchant un peu, on s&amp;rsquo;aperçoit que les jours fériés, c&amp;rsquo;est pas si simple que ca !&lt;/p>
&lt;p>En effet, rien qu&amp;rsquo;en France, ce n&amp;rsquo;est pas si simple : il y a les jours fixes, et les jours mobiles. Il faut effectuer un calcul compliqué pour obtenir le jour de Pâques et les jours calculés relativement à Pâques (jeudi de l&amp;rsquo;Ascension et lundi de Pentecôte).&lt;/p></description><content:encoded>
<![CDATA[<p>Toujours dans l&rsquo;optique de créer un <strong>Agenda partagé</strong> pour l&rsquo;application <strong>OpenProspect</strong>, il me faut mettre sur mon calendrier&hellip; les <strong>jours fériés</strong> ! Jusque là, tout va bien&hellip; Mais en cherchant un peu, on s&rsquo;aperçoit que les jours fériés, c&rsquo;est pas si simple que ca !</p>
<p>En effet, rien qu&rsquo;en France, ce n&rsquo;est pas si simple : il y a les jours fixes, et les jours mobiles. Il faut effectuer un calcul compliqué pour obtenir le jour de Pâques et les jours calculés relativement à Pâques (jeudi de l&rsquo;Ascension et lundi de Pentecôte).</p>
<p>Mais cela se complique, car en dehors de France, dans certains pays, les jours fériés ne sont pas les mêmes suivant les régions ! Aux Etats-Unis par exemple, il existe des jours fériés spécifiques à un état particulier !</p>
<p>On trouve des ressources intéressantes sur Internet :</p>
<ul>
<li>
<p>Un <a href="http://www.jours-feries.com/">site excellent</a> pour calculer TOUS les jours fériés d&rsquo;un très grand nombre de pays,</p>
</li>
<li>
<p>Un autre <a href="http://www.bdl.fr/minitel/calendrier/date_paques.html">calcul du Jour de Pâques</a> en Javascript (regarder le code source de la page HTML)</p>
</li>
</ul>
<p>Bref, si l&rsquo;on souhaite afficher sur un calendrier tous les jours fériés, on n&rsquo;est pas sorti de la veille ;-). Bon courage !</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Apache XML-RPC, côté client</title><link>https://glaforge.dev/posts/2003/04/03/apache-xml-rpc-c-t-client/</link><pubDate>Thu, 03 Apr 2003 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2003/04/03/apache-xml-rpc-c-t-client/</guid><description>&lt;p>Pour le fun, j&amp;rsquo;ai eu envie d&amp;rsquo;essayer l&amp;rsquo;&lt;a href="http://ws.apache.org/xmlrpc/">API XML-RPC de la fondation Apache&lt;/a>. Pour être précis, je vouais expérimenter avec les APIs de Blogger et MetaWeblog qui permettent de mettre à jour les Weblogs à distance. Ce sont des APIs standardisées que la pluspart des Weblog comprennent.&lt;/p>
&lt;p>Ainsi, par exemple, l&amp;rsquo;outil de publication &lt;a href="http://wbloggar.com/">w.Bloggar&lt;/a> permet de éditer/modifier/créer des nouvelles entrées dans votre blog. Pour cela, il fait appel aux procédures distantes (Remote Procedure) du serveur hébergeant votre blog.&lt;/p></description><content:encoded>
<![CDATA[<p>Pour le fun, j&rsquo;ai eu envie d&rsquo;essayer l&rsquo;<a href="http://ws.apache.org/xmlrpc/">API XML-RPC de la fondation Apache</a>. Pour être précis, je vouais expérimenter avec les APIs de Blogger et MetaWeblog qui permettent de mettre à jour les Weblogs à distance. Ce sont des APIs standardisées que la pluspart des Weblog comprennent.</p>
<p>Ainsi, par exemple, l&rsquo;outil de publication <a href="http://wbloggar.com/">w.Bloggar</a> permet de éditer/modifier/créer des nouvelles entrées dans votre blog. Pour cela, il fait appel aux procédures distantes (Remote Procedure) du serveur hébergeant votre blog.</p>
<p>Pour le cas qui m&rsquo;intéresse, j&rsquo;utilise chez mon hébergeur le Blog Nucleus. C&rsquo;est un blog GPL, donc gratuit, avec toutes les fonctionnalités que l&rsquo;on peut attendre d&rsquo;un tel outil de publication (multi-weblog, interface d&rsquo;administration, multi-utilisateur, ban IP, commentaires, plugin divers et variés, etc&hellip;)</p>
<p>J&rsquo;ai donc téléchargé l&rsquo;API XML-RPC d&rsquo;Apache, j&rsquo;ai lancé mon IDE Java préféré (<a href="http://www.intellij.com/">IntelliJ IDEA</a>), j&rsquo;ai créé un nouveau projet avec une petite classe NucleusClient et sa méthode main() pour tester Nucleus et son support des APIs de weblogging&hellip;</p>
<p>C&rsquo;est déconcertant de facilité ! Vive XML-RPC ! Jugez-en par vous-même :</p>
<div class="highlight"><pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">org.apache.xmlrpc.XmlRpcClient</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">org.apache.xmlrpc.XmlRpcException</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.util.Vector</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">import</span><span style="color:#bbb"> </span><span style="color:#0e84b5;font-weight:bold">java.io.IOException</span>;<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">class</span> <span style="color:#0e84b5;font-weight:bold">NucleusClient</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span><span style="color:#007020;font-weight:bold">public</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">static</span><span style="color:#bbb"> </span><span style="color:#902000">void</span><span style="color:#bbb"> </span><span style="color:#06287e">main</span>(String<span style="color:#666">[]</span><span style="color:#bbb"> </span>args)<span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">throws</span><span style="color:#bbb"> </span>IOException,<span style="color:#bbb"> </span>XmlRpcException<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">    </span>{<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>XmlRpcClient<span style="color:#bbb"> </span>rpcClient<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> 
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">            </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>XmlRpcClient(<span style="color:#4070a0">&#34;http://www.yourhost.com/weblog/nucleus/xmlrpc/server.php&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>Vector<span style="color:#bbb"> </span>params<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span><span style="color:#007020;font-weight:bold">new</span><span style="color:#bbb"> </span>Vector();<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>params.<span style="color:#4070a0">addElement</span>(<span style="color:#4070a0">&#34;1&#34;</span>);<span style="color:#bbb"> </span><span style="color:#60a0b0;font-style:italic">// blogId</span><span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>params.<span style="color:#4070a0">addElement</span>(<span style="color:#4070a0">&#34;YourAccount&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>params.<span style="color:#4070a0">addElement</span>(<span style="color:#4070a0">&#34;YourPassword&#34;</span>);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>String<span style="color:#bbb"> </span>result<span style="color:#bbb"> </span><span style="color:#666">=</span><span style="color:#bbb"> </span>(String)rpcClient.<span style="color:#4070a0">execute</span>(<span style="color:#4070a0">&#34;metaWeblog.getPost&#34;</span>,<span style="color:#bbb"> </span>params);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">        </span>System.<span style="color:#4070a0">out</span>.<span style="color:#4070a0">println</span>(<span style="color:#4070a0">&#34;result = &#34;</span><span style="color:#bbb"> </span><span style="color:#666">+</span><span style="color:#bbb"> </span>result);<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb">  </span>}<span style="color:#bbb">
</span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>}<span style="color:#bbb">
</span></span></span></code></pre></div><p>En quelques lignes, on appelle un Service Web (reposant sur XML-RPC). On utilise pour cela la classe XmlRpcClient à qui l&rsquo;on passe l&rsquo;URL du blog, on crée un vecteur contenant les paramètres de la méthode distante, et on execute la requête. Il ne reste plus qu&rsquo;à récupérer le résultat !</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Les jours de la semaine</title><link>https://glaforge.dev/posts/2003/04/02/les-jours-de-la-semaine/</link><pubDate>Wed, 02 Apr 2003 00:00:00 +0200</pubDate><guid>https://glaforge.dev/posts/2003/04/02/les-jours-de-la-semaine/</guid><description>&lt;p>D&amp;rsquo;après ce qu&amp;rsquo;on peut lire dans la &lt;a href="http://www.auduteau.net/calendar/cal5.shtml#5_7">FAQ des calendriers&lt;/a> :&lt;/p>
&lt;blockquote>
&lt;p>La norme internationale ISO-8601 (mentionnée dans la section 5.6) assigne un numéro à chaque semaine de l&amp;rsquo;année. Une semaine chevauchant deux années se voit assigné un numéro dans l&amp;rsquo;année qui contient le plus grand nombre de ses jours.&lt;/p>
&lt;p>Ce qui revient à dire que La semaine 1 de toute année est celle qui contient le 4 janvier ou que la semaine 1 de toute année est celle qui contient le 1er jeudi de janvier.&lt;/p></description><content:encoded>
<![CDATA[<p>D&rsquo;après ce qu&rsquo;on peut lire dans la <a href="http://www.auduteau.net/calendar/cal5.shtml#5_7">FAQ des calendriers</a> :</p>
<blockquote>
<p>La norme internationale ISO-8601 (mentionnée dans la section 5.6) assigne un numéro à chaque semaine de l&rsquo;année. Une semaine chevauchant deux années se voit assigné un numéro dans l&rsquo;année qui contient le plus grand nombre de ses jours.</p>
<p>Ce qui revient à dire que La semaine 1 de toute année est celle qui contient le 4 janvier ou que la semaine 1 de toute année est celle qui contient le 1er jeudi de janvier.</p>
<p>La majorité des années ont 52 semaines mais les années qui commence un jeudi et les années bissextiles commençant un mercredi en possède 53.</p></blockquote>
<p>En d&rsquo;autres termes, le 1er janvier peut-être soit dans la semaine 1, soit dans la semaine 52 ou 53, c&rsquo;est-à-dire dans la dernière semaine de l&rsquo;année d&rsquo;avant.</p>
<p>Mais en Java, qui se base soi-disant sur la norme ISO-8601, il se trouve que le 1er janvier est <strong>toujours</strong> dans la semaine numéro 1.</p>
<p>Qui a tort ? Qui a raison ?</p>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>About</title><link>https://glaforge.dev/about/</link><pubDate>Wed, 01 Jan 2003 00:14:56 +0100</pubDate><guid>https://glaforge.dev/about/</guid><description>&lt;p>&lt;strong>Guillaume Laforge&lt;/strong> is &lt;strong>Developer Advocate&lt;/strong> for &lt;a href="https://cloud.google.com/">Google Cloud&lt;/a>,
where he spreads the word to developers about the serverless compute space, service orchestration solutions, and &lt;strong>generative AI&lt;/strong>.&lt;/p>
&lt;p>Guillaume is a &lt;a href="https://javachampions.org/">Java Champion&lt;/a>,
a co-author of &lt;a href="https://www.manning.com/books/groovy-in-action-second-edition">Groovy in Action&lt;/a>,
and a founding member of the French tech podcast &lt;a href="http://lescastcodeurs.com/">Les CastCodeurs&lt;/a>.&lt;/p>
&lt;p>Among other open source contributions, Guillaume is a committer &lt;a href="https://docs.langchain4j.dev/">LangChain4j&lt;/a>
and &lt;a href="https://github.com/google/adk-java">ADK for Java&lt;/a>, for building the next generation LLM powered applications and AI agents.&lt;/p>
&lt;p>Guillaume is the co-founder of the &lt;a href="http://groovy.codehaus.org/">Apache Groovy&lt;/a> programming language project,
which he led under the umbrella of G2One (a Groovy/Grails startup where he was the &lt;strong>VP Technology&lt;/strong>), then SpringSource, VMware, and the Pivotal spin-off. 
When the project joined to the Apache Software Foundation, he was the Chair of the Apache Groovy Project Management Committee (PMC).
He is a member of the &lt;a href="http://micronaut.io/">Micronaut&lt;/a> Technology Advisory Board.
He initiated the creation of the &lt;a href="http://grails.org/">Grails&lt;/a> web application framework,
and founded the &lt;a href="http://gaelyk.appspot.com/">Gaelyk&lt;/a> project, a lightweight toolkit for developing applications in Groovy for Google App Engine.&lt;/p></description><content:encoded>
<![CDATA[<p><strong>Guillaume Laforge</strong> is <strong>Developer Advocate</strong> for <a href="https://cloud.google.com/">Google Cloud</a>,
where he spreads the word to developers about the serverless compute space, service orchestration solutions, and <strong>generative AI</strong>.</p>
<p>Guillaume is a <a href="https://javachampions.org/">Java Champion</a>,
a co-author of <a href="https://www.manning.com/books/groovy-in-action-second-edition">Groovy in Action</a>,
and a founding member of the French tech podcast <a href="http://lescastcodeurs.com/">Les CastCodeurs</a>.</p>
<p>Among other open source contributions, Guillaume is a committer <a href="https://docs.langchain4j.dev/">LangChain4j</a>
and <a href="https://github.com/google/adk-java">ADK for Java</a>, for building the next generation LLM powered applications and AI agents.</p>
<p>Guillaume is the co-founder of the <a href="http://groovy.codehaus.org/">Apache Groovy</a> programming language project,
which he led under the umbrella of G2One (a Groovy/Grails startup where he was the <strong>VP Technology</strong>), then SpringSource, VMware, and the Pivotal spin-off. 
When the project joined to the Apache Software Foundation, he was the Chair of the Apache Groovy Project Management Committee (PMC).
He is a member of the <a href="http://micronaut.io/">Micronaut</a> Technology Advisory Board.
He initiated the creation of the <a href="http://grails.org/">Grails</a> web application framework,
and founded the <a href="http://gaelyk.appspot.com/">Gaelyk</a> project, a lightweight toolkit for developing applications in Groovy for Google App Engine.</p>
<p>He is a <strong>frequent conference speaker</strong> <a href="/talks">presenting</a> on serverless technologies, microservice orchestration and architecture,
machine learning, conversational interfaces, Google Cloud projects &amp; services, Apache Groovy, Micronaut, Web API design,
at events like Devoxx and Voxxed Days, JavaOne, JAX, GR8Conf, Greach, DevTernity, QCon, Cloud Next, among others.</p>
<p>Before joining Google, at Restlet (acquired by <a href="https://www.talend.com/">Talend</a>),
Guillaume was taking care of the <strong>Product Leadership</strong> of the products, and was <strong>leading Developer Advocacy</strong>.
Prior to that, he was leading the Groovy team at G2One, SpringSource, VMware and Pivotal.
Before working full-time on Groovy, Guillaume worked as a <strong>Software Architect</strong> for <a href="http://www.octo.com/">OCTO Technology</a>,
a consultancy focusing on architecture and agile methodologies, where he developed new customer offerings around Groovy and Grails.</p>
<h2 id="contact">Contact</h2>
<table>
  <thead>
      <tr>
          <th style="text-align: right"></th>
          <th></th>
          <th></th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: right"><figure>
  <a href="#img-6619f734c7219c74bb3e0c768c4e7c1a">
    <img src="/img/icons/bluesky-black.svg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-6619f734c7219c74bb3e0c768c4e7c1a">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/icons/bluesky-black.svg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</td>
          <td></td>
          <td><a href="https://bsky.app/profile/glaforge.dev">glaforge.dev</a></td>
      </tr>
      <tr>
          <td style="text-align: right"><figure>
  <a href="#img-e32349797e2801a62909f1416b3356dc">
    <img src="/img/icons/twitter-black.svg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-e32349797e2801a62909f1416b3356dc">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/icons/twitter-black.svg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</td>
          <td></td>
          <td><a href="https://twitter.com/glaforge">twitter.com/glaforge</a></td>
      </tr>
      <tr>
          <td style="text-align: right"><figure>
  <a href="#img-6050971dbd8b61802b8293467b88a106">
    <img src="/img/icons/mastodon-black.svg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-6050971dbd8b61802b8293467b88a106">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/icons/mastodon-black.svg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</td>
          <td></td>
          <td><a href="https://uwyn.net/@glaforge">@uwyn.net/@glaforge</a></td>
      </tr>
      <tr>
          <td style="text-align: right"><figure>
  <a href="#img-5538d91ffbb4f6f3598894bc9d69668a">
    <img src="/img/icons/linkedin-black.svg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-5538d91ffbb4f6f3598894bc9d69668a">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/icons/linkedin-black.svg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</td>
          <td></td>
          <td><a href="https://www.linkedin.com/in/glaforge">linkedin.com/in/glaforge</a></td>
      </tr>
      <tr>
          <td style="text-align: right"><figure>
  <a href="#img-0445652c6da36757d211f36da5089ea9">
    <img src="/img/icons/github-black.svg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-0445652c6da36757d211f36da5089ea9">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/icons/github-black.svg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</td>
          <td></td>
          <td><a href="https://github.com/glaforge">github.com/glaforge</a></td>
      </tr>
      <tr>
          <td style="text-align: right"><figure>
  <a href="#img-517f370542ee064de793a27587d22895">
    <img src="/img/icons/speakerdeck-black.svg"
      alt=""
       />
  </a>
  <figcaption></figcaption>
</figure>
<div class="lightbox" id="img-517f370542ee064de793a27587d22895">
  <a href="#_" class="lightbox-overlay"></a>
  <img src="/img/icons/speakerdeck-black.svg"
    alt=""
     />
  <div class="lightbox-caption"></div>
</div>
</td>
          <td></td>
          <td><a href="https://speakerdeck.com/glaforge">speakerdeck.com/glaforge</a></td>
      </tr>
  </tbody>
</table>
<img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" />]]></content:encoded></item><item><title>Archive</title><link>https://glaforge.dev/archive/</link><pubDate>Wed, 01 Jan 2003 00:14:56 +0100</pubDate><guid>https://glaforge.dev/archive/</guid><description/><content:encoded>
&lt;img src="https://pixstat-adh43sr7ba-ew.a.run.app/transparent.gif" width="1" height="1" /></content:encoded></item></channel></rss>